blob: 16b1f2fafd2cd25a6a04c310326020c7b5105716 [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
25#include "common.h"
Michal Vaskoe78faec2021-04-08 17:24:43 +020026#include "compat.h"
Radek Krejci47fab892020-11-05 17:02:41 +010027#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020028#include "log.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 Vaskod59035b2020-07-08 12:00:06 +0200294
295 assert(diff);
296
Michal Vasko53d48422020-11-13 18:02:29 +0100297 /* replace leaf always needs orig-default and orig-value */
298 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
299
300 /* create on userord needs key/value */
301 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200302 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100303 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200304 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100305
306 /* move on userord needs both key and orig-key/value and orig-value */
307 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200308 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100309 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200310 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
311 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100312
Michal Vaskod59035b2020-07-08 12:00:06 +0200313 /* find the first existing parent */
314 siblings = *diff;
Michal Vaskofb51a842023-06-20 08:50:24 +0200315 do {
Michal Vaskod59035b2020-07-08 12:00:06 +0200316 /* find next node parent */
317 parent = node;
318 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100319 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200320 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200321
322 if (lysc_is_dup_inst_list(parent->schema)) {
323 /* assume it never exists, we are not able to distinguish whether it does or not */
324 match = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200325 break;
326 }
327
328 /* check whether it exists in the diff */
329 if (lyd_find_sibling_first(siblings, parent, &match)) {
330 break;
331 }
332
333 /* another parent found */
334 diff_parent = match;
335
336 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200337 siblings = lyd_child_no_keys(match);
Michal Vaskofb51a842023-06-20 08:50:24 +0200338 } while (parent != node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200339
Michal Vaskofb51a842023-06-20 08:50:24 +0200340 if (match && (parent == node)) {
341 /* special case when there is already an operation on our descendant */
342 assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
343 (void)cur_op;
344
Michal Vaskod4525672023-06-20 09:15:42 +0200345 /* will be replaced by the new operation but keep the current op for descendants */
Michal Vaskofb51a842023-06-20 08:50:24 +0200346 lyd_diff_del_meta(diff_parent, "operation");
Michal Vaskod4525672023-06-20 09:15:42 +0200347 LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
348 lyd_diff_find_meta(elem, "operation", &meta, NULL);
349 if (meta) {
350 /* explicit operation, fine */
351 continue;
352 }
353
354 /* set the none operation */
355 LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", 0, NULL));
356 }
357
Michal Vaskofb51a842023-06-20 08:50:24 +0200358 dup = diff_parent;
Michal Vasko695a7f22023-02-14 10:02:10 +0100359 } else {
Michal Vaskofb51a842023-06-20 08:50:24 +0200360 /* duplicate the subtree (and connect to the diff if possible) */
361 if (diff_parent) {
362 LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
363 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
364 } else {
365 LY_CHECK_RET(lyd_dup_single(node, NULL,
366 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200367 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200368
369 /* find the first duplicated parent */
370 if (!diff_parent) {
371 diff_parent = lyd_parent(dup);
372 while (diff_parent && diff_parent->parent) {
373 diff_parent = lyd_parent(diff_parent);
374 }
375 } else {
376 diff_parent = dup;
377 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
378 diff_parent = lyd_parent(diff_parent);
379 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200380 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200381
Michal Vaskofb51a842023-06-20 08:50:24 +0200382 /* no parent existed, must be manually connected */
383 if (!diff_parent) {
384 /* there actually was no parent to duplicate */
385 lyd_insert_sibling(*diff, dup, diff);
386 } else if (!diff_parent->parent) {
387 lyd_insert_sibling(*diff, diff_parent, diff);
388 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200389
Michal Vaskofb51a842023-06-20 08:50:24 +0200390 /* add parent operation, if any */
391 if (diff_parent && (diff_parent != dup)) {
392 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
393 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200394 }
395
396 /* add subtree operation */
Michal Vasko2e552792022-11-02 12:15:31 +0100397 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200398
Michal Vaskocffc3f92022-06-15 07:57:24 +0200399 if (op == LYD_DIFF_OP_CREATE) {
400 /* all nested user-ordered (leaf-)lists need special metadata for create op */
401 LYD_TREE_DFS_BEGIN(dup, elem) {
402 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
403 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
404 }
405 LYD_TREE_DFS_END(dup, elem);
406 }
407 }
408
Michal Vaskod59035b2020-07-08 12:00:06 +0200409 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200410 if (orig_default) {
Michal Vasko2e552792022-11-02 12:15:31 +0100411 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200412 }
413
414 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200415 if (orig_value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100416 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200417 }
418
419 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200420 if (key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100421 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200422 }
423
424 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200425 if (value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100426 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200427 }
428
Michal Vaskoe78faec2021-04-08 17:24:43 +0200429 /* position */
430 if (position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100431 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200432 }
433
Michal Vaskod59035b2020-07-08 12:00:06 +0200434 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200435 if (orig_key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100436 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200437 }
438
Michal Vaskoe78faec2021-04-08 17:24:43 +0200439 /* orig-position */
440 if (orig_position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100441 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200442 }
443
Michal Vaskod59035b2020-07-08 12:00:06 +0200444 return LY_SUCCESS;
445}
446
447/**
448 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
449 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100450 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200451 * @param[in] schema Schema node of the list/leaf-list.
452 * @param[in,out] userord Sized array of userord items.
453 * @return Userord item for all the user-ordered list/leaf-list instances.
454 */
455static struct lyd_diff_userord *
456lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
457{
458 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200459 struct lyd_node *iter;
460 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200461 LY_ARRAY_COUNT_TYPE u;
462
463 LY_ARRAY_FOR(*userord, u) {
464 if ((*userord)[u].schema == schema) {
465 return &(*userord)[u];
466 }
467 }
468
469 /* it was not added yet, add it now */
470 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
471
472 item->schema = schema;
473 item->pos = 0;
474 item->inst = NULL;
475
476 /* store all the instance pointers in the current order */
477 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200478 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
479 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
480 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200481 }
482 }
483
484 return item;
485}
486
487/**
488 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
489 * lists/leaf-lists.
490 *
491 * @param[in] first Node from the first tree, can be NULL (on create).
492 * @param[in] second Node from the second tree, can be NULL (on delete).
493 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100494 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200495 * @param[out] op Operation.
496 * @param[out] orig_default Original default metadata.
497 * @param[out] value Value metadata.
498 * @param[out] orig_value Original value metadata
499 * @param[out] key Key metadata.
500 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200501 * @param[out] position Position metadata.
502 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200503 * @return LY_SUCCESS on success,
504 * @return LY_ENOT if there is no change to be added into diff,
505 * @return LY_ERR value on other errors.
506 */
507static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200508lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100509 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200510 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200511{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200512 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200513 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200514 size_t buflen, bufused;
Michal Vaskoee9b9482023-06-19 13:17:48 +0200515 uint32_t first_pos, second_pos, comp_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200516
517 assert(first || second);
518
519 *orig_default = NULL;
520 *value = NULL;
521 *orig_value = NULL;
522 *key = NULL;
523 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200524 *position = NULL;
525 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200526
527 schema = first ? first->schema : second->schema;
528 assert(lysc_is_userordered(schema));
529
Michal Vaskod59035b2020-07-08 12:00:06 +0200530 /* find user-ordered first position */
531 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200532 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200533 if (userord_item->inst[first_pos] == first) {
534 break;
535 }
536 }
537 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
538 } else {
539 first_pos = 0;
540 }
541
Michal Vaskoe78faec2021-04-08 17:24:43 +0200542 /* prepare position of the next instance */
543 second_pos = userord_item->pos++;
544
Michal Vaskod59035b2020-07-08 12:00:06 +0200545 /* learn operation first */
546 if (!second) {
547 *op = LYD_DIFF_OP_DELETE;
548 } else if (!first) {
549 *op = LYD_DIFF_OP_CREATE;
550 } else {
Michal Vaskoee9b9482023-06-19 13:17:48 +0200551 comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
552 if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200553 /* in first, there is a different instance on the second position, we are going to move 'first' node */
554 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200555 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200556 /* default flag change */
557 *op = LYD_DIFF_OP_NONE;
558 } else {
559 /* no changes */
560 return LY_ENOT;
561 }
562 }
563
564 /*
565 * set each attribute correctly based on the operation and node type
566 */
567
568 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100569 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200570 if (first->flags & LYD_DEFAULT) {
571 *orig_default = "true";
572 } else {
573 *orig_default = "false";
574 }
575 }
576
577 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200578 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
579 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200580 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200581 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200582 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200583 } else {
584 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200585 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200586 }
587 }
588
589 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200590 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
591 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200592 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200593 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200594 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200595 } else {
596 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200597 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200598 }
599 }
600
601 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200602 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
603 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200604 if (second_pos) {
605 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200606 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 +0200607 } else {
608 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200609 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200610 }
611 }
612
613 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200614 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
615 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200616 if (first_pos) {
617 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200618 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 +0200619 } else {
620 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200621 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200622 }
623 }
624
Michal Vaskoe78faec2021-04-08 17:24:43 +0200625 /* position */
626 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
627 if (second_pos) {
628 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
629 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200630 rc = LY_EMEM;
631 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200632 }
633 } else {
634 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200635 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200636 }
637 }
638
639 /* orig-position */
640 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
641 if (first_pos) {
642 if (asprintf(orig_position, "%" PRIu32, first_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 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200649 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200650 }
651 }
652
Michal Vaskod59035b2020-07-08 12:00:06 +0200653 /*
654 * update our instances - apply the change
655 */
656 if (*op == LYD_DIFF_OP_CREATE) {
657 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200658 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200659 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
660 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
661 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
662 }
663 LY_ARRAY_INCREMENT(userord_item->inst);
664 userord_item->inst[second_pos] = second;
665
666 } else if (*op == LYD_DIFF_OP_DELETE) {
667 /* remove the instance */
668 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
669 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
670 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
671 }
672 LY_ARRAY_DECREMENT(userord_item->inst);
673
674 } else if (*op == LYD_DIFF_OP_REPLACE) {
675 /* move the instances */
676 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
677 (first_pos - second_pos) * sizeof *userord_item->inst);
678 userord_item->inst[second_pos] = first;
679 }
680
Michal Vaskof9b052a2022-06-08 10:26:53 +0200681cleanup:
682 if (rc) {
683 free(*value);
684 *value = NULL;
685 free(*orig_value);
686 *orig_value = NULL;
687 free(*key);
688 *key = NULL;
689 free(*orig_key);
690 *orig_key = NULL;
691 free(*position);
692 *position = NULL;
693 free(*orig_position);
694 *orig_position = NULL;
695 }
696 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200697}
698
699/**
700 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
701 * lists/leaf-lists.
702 *
703 * @param[in] first Node from the first tree, can be NULL (on create).
704 * @param[in] second Node from the second tree, can be NULL (on delete).
705 * @param[in] options Diff options.
706 * @param[out] op Operation.
707 * @param[out] orig_default Original default metadata.
708 * @param[out] orig_value Original value metadata.
709 * @return LY_SUCCESS on success,
710 * @return LY_ENOT if there is no change to be added into diff,
711 * @return LY_ERR value on other errors.
712 */
713static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200714lyd_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 +0200715 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200716{
717 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200718 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200719
720 assert(first || second);
721
722 *orig_default = NULL;
723 *orig_value = NULL;
724
725 schema = first ? first->schema : second->schema;
726 assert(!lysc_is_userordered(schema));
727
728 /* learn operation first */
729 if (!second) {
730 *op = LYD_DIFF_OP_DELETE;
731 } else if (!first) {
732 *op = LYD_DIFF_OP_CREATE;
733 } else {
734 switch (schema->nodetype) {
735 case LYS_CONTAINER:
736 case LYS_RPC:
737 case LYS_ACTION:
738 case LYS_NOTIF:
739 /* no changes */
740 return LY_ENOT;
741 case LYS_LIST:
742 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200743 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200744 /* default flag change */
745 *op = LYD_DIFF_OP_NONE;
746 } else {
747 /* no changes */
748 return LY_ENOT;
749 }
750 break;
751 case LYS_LEAF:
752 case LYS_ANYXML:
753 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200754 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200755 /* different values */
756 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200757 } else 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 default:
766 LOGINT_RET(schema->module->ctx);
767 }
768 }
769
770 /*
771 * set each attribute correctly based on the operation and node type
772 */
773
774 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100775 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200776 if (first->flags & LYD_DEFAULT) {
777 *orig_default = "true";
778 } else {
779 *orig_default = "false";
780 }
781 }
782
783 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100784 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
785 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200786 str_val = lyd_get_value(first);
787 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100788 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
789 } else {
790 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
791 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200792 }
793
794 return LY_SUCCESS;
795}
796
797/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200798 * @brief Find a matching instance of a node in a data tree.
799 *
800 * @param[in] siblings Siblings to search in.
801 * @param[in] target Target node to search for.
802 * @param[in] defaults Whether to consider (or ignore) default values.
Michal Vasko271d2e32023-01-31 15:43:19 +0100803 * @param[in,out] dup_inst_ht Duplicate instance cache.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200804 * @param[out] match Found match, NULL if no matching node found.
805 * @return LY_ERR value.
806 */
807static LY_ERR
808lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vasko8efac242023-03-30 08:24:56 +0200809 struct ly_ht **dup_inst_ht, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200810{
Michal Vasko2bd856f2022-12-02 14:03:29 +0100811 LY_ERR r;
812
Michal Vaskodb91fc32023-05-02 14:39:40 +0200813 if (!target->schema) {
814 /* try to find the same opaque node */
815 r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
816 } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200817 /* try to find the exact instance */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100818 r = lyd_find_sibling_first(siblings, target, match);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200819 } else {
820 /* try to simply find the node, there cannot be more instances */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100821 r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
822 }
823 if (r && (r != LY_ENOTFOUND)) {
824 return r;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200825 }
826
Michal Vaskod7c048c2021-05-18 16:12:55 +0200827 /* update match as needed */
Michal Vasko271d2e32023-01-31 15:43:19 +0100828 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200829
830 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
831 /* ignore default nodes */
832 *match = NULL;
833 }
834 return LY_SUCCESS;
835}
836
837/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200838 * @brief Perform diff for all siblings at certain depth, recursively.
839 *
840 * For user-ordered lists/leaf-lists a specific structure is used for storing
841 * the current order. The idea is to apply all the generated diff changes
842 * virtually on the first tree so that we can continue to generate correct
843 * changes after some were already generated.
844 *
845 * The algorithm then uses second tree position-based changes with a before
846 * (preceding) item anchor.
847 *
848 * Example:
849 *
850 * Virtual first tree leaf-list order:
851 * 1 2 [3] 4 5
852 *
853 * Second tree leaf-list order:
854 * 1 2 [5] 3 4
855 *
856 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
857 * match - they do not - move nodes so that the 3rd position node is final ->
858 * -> move node 5 to the 3rd position -> move node 5 after node 2.
859 *
860 * Required properties:
861 * Stored operations (move) should not be affected by later operations -
862 * - would cause a redundantly long list of operations, possibly inifinite.
863 *
864 * Implemenation justification:
865 * First, all delete operations and only then move/create operations are stored.
866 * Also, preceding anchor is used and after each iteration another node is
867 * at its final position. That results in the invariant that all preceding
868 * nodes are final and will not be changed by the later operations, meaning
869 * they can safely be used as anchors for the later operations.
870 *
871 * @param[in] first First tree first sibling.
872 * @param[in] second Second tree first sibling.
873 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200874 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200875 * @param[in,out] diff Diff to append to.
876 * @return LY_ERR value.
877 */
878static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200879lyd_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 +0200880 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200881{
882 LY_ERR ret = LY_SUCCESS;
883 const struct lyd_node *iter_first, *iter_second;
884 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100885 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vasko8efac242023-03-30 08:24:56 +0200886 struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 LY_ARRAY_COUNT_TYPE u;
888 enum lyd_diff_op op;
889 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200890 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200891
Michal Vaskod59035b2020-07-08 12:00:06 +0200892 /* compare first tree to the second tree - delete, replace, none */
893 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200894 if (!iter_first->schema) {
895 continue;
896 }
897
Michal Vaskod59035b2020-07-08 12:00:06 +0200898 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200899 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200900 /* skip default nodes */
901 continue;
902 }
903
Michal Vaskoe78faec2021-04-08 17:24:43 +0200904 /* find a match in the second tree */
905 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
906 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200907
908 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100909 /* get (create) userord entry */
910 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
911 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
912
Michal Vaskoe78faec2021-04-08 17:24:43 +0200913 /* we are handling only user-ordered node delete now */
914 if (!match_second) {
915 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100916 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
917 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200918
919 /* there must be changes, it is deleted */
920 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100921 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
922 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200923
924 free(orig_value);
925 free(key);
926 free(value);
927 free(position);
928 free(orig_key);
929 free(orig_position);
930 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200931 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200932 } else {
933 /* get all the attributes */
934 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
935
936 /* add into diff if there are any changes */
937 if (!ret) {
938 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200939 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100941 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200942 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200943 }
944
945 free(orig_value);
946 LY_CHECK_GOTO(ret, cleanup);
947 } else if (ret == LY_ENOT) {
948 ret = LY_SUCCESS;
949 } else {
950 goto cleanup;
951 }
952 }
953
954 /* check descendants, if any, recursively */
955 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200956 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
957 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200958 }
959
960 if (nosiblings) {
961 break;
962 }
963 }
964
965 /* reset all cached positions */
966 LY_ARRAY_FOR(userord, u) {
967 userord[u].pos = 0;
968 }
969
970 /* compare second tree to the first tree - create, user-ordered move */
971 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200972 if (!iter_second->schema) {
973 continue;
974 }
975
Michal Vaskod59035b2020-07-08 12:00:06 +0200976 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200977 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200978 /* skip default nodes */
979 continue;
980 }
981
Michal Vaskoe78faec2021-04-08 17:24:43 +0200982 /* find a match in the first tree */
983 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
984 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200985
986 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100987 /* get userord entry */
Michal Vaskoaa51cc52022-12-06 09:57:11 +0100988 userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord);
Michal Vasko5da938a2022-03-01 09:19:02 +0100989 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
990
Michal Vaskod59035b2020-07-08 12:00:06 +0200991 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100992 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200993 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200994
995 /* add into diff if there are any changes */
996 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200997 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
998 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200999
1000 free(orig_value);
1001 free(key);
1002 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001003 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001004 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001005 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001006 LY_CHECK_GOTO(ret, cleanup);
1007 } else if (ret == LY_ENOT) {
1008 ret = LY_SUCCESS;
1009 } else {
1010 goto cleanup;
1011 }
1012 } else if (!match_first) {
1013 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001014 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 +02001015
1016 /* there must be changes, it is created */
1017 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001018 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001019
1020 free(orig_value);
1021 LY_CHECK_GOTO(ret, cleanup);
1022 } /* else was handled */
1023
1024 if (nosiblings) {
1025 break;
1026 }
1027 }
1028
1029cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001030 lyd_dup_inst_free(dup_inst_first);
1031 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001032 LY_ARRAY_FOR(userord, u) {
1033 LY_ARRAY_FREE(userord[u].inst);
1034 }
1035 LY_ARRAY_FREE(userord);
1036 return ret;
1037}
1038
Michal Vasko3a41dff2020-07-15 14:30:28 +02001039static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +01001040lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
1041 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001042{
1043 const struct ly_ctx *ctx;
1044
1045 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1046
1047 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001048 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +02001049 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001050 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001051 } else {
1052 ctx = NULL;
1053 }
1054
1055 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
1056 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
1057 return LY_EINVAL;
1058 }
1059
1060 *diff = NULL;
1061
Michal Vasko3a41dff2020-07-15 14:30:28 +02001062 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
1063}
1064
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001065LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001066lyd_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 +02001067{
1068 return lyd_diff(first, second, options, 1, diff);
1069}
1070
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001071LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001072lyd_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 +02001073{
1074 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001075}
1076
1077/**
Michal Vaskod59035b2020-07-08 12:00:06 +02001078 * @brief Insert a diff node into a data tree.
1079 *
1080 * @param[in,out] first_node First sibling of the data tree.
1081 * @param[in] parent_node Data tree sibling parent node.
1082 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001083 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
1084 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +02001085 * @return err_info, NULL on success.
1086 */
1087static LY_ERR
1088lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001089 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +02001090{
1091 LY_ERR ret;
1092 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001093 uint32_t pos, anchor_pos;
1094 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +02001095
1096 assert(new_node);
1097
1098 if (!*first_node) {
1099 if (!parent_node) {
1100 /* no parent or siblings */
1101 *first_node = new_node;
1102 return LY_SUCCESS;
1103 }
1104
1105 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001106 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001107 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +02001108 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +02001109 return LY_EINVAL;
1110 }
Michal Vaskob104f112020-07-17 09:54:54 +02001111 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001112 }
1113
Michal Vasko9e685082021-01-29 14:49:09 +01001114 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001115
Michal Vaskod59035b2020-07-08 12:00:06 +02001116 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001117 /* simple insert */
1118 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001119 }
1120
Michal Vaskoe78faec2021-04-08 17:24:43 +02001121 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001122 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001123 if (lysc_is_dup_inst_list(new_node->schema)) {
1124 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001125 if (!anchor_pos) {
1126 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1127 return LY_EINVAL;
1128 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001129
1130 found = 0;
1131 pos = 1;
1132 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1133 if (pos == anchor_pos) {
1134 found = 1;
1135 break;
1136 }
1137 ++pos;
1138 }
1139 if (!found) {
1140 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1141 new_node->schema->name);
1142 return LY_EINVAL;
1143 }
1144 } else {
1145 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1146 if (ret == LY_ENOTFOUND) {
1147 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1148 new_node->schema->name);
1149 return LY_EINVAL;
1150 } else if (ret) {
1151 return ret;
1152 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001153 }
1154
1155 /* insert after */
1156 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1157 assert(new_node->prev == anchor);
1158 if (*first_node == new_node) {
1159 *first_node = anchor;
1160 }
1161 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001162 /* find the first instance */
1163 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1164 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001165
Michal Vaskoea7d3232022-04-19 12:01:36 +02001166 if (anchor) {
1167 /* insert before the first instance */
1168 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1169 if ((*first_node)->prev->next) {
1170 assert(!new_node->prev->next);
1171 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001172 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001173 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001174 /* insert anywhere */
1175 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001176 }
1177 }
1178
1179 return LY_SUCCESS;
1180}
1181
1182/**
1183 * @brief Apply diff subtree on data tree nodes, recursively.
1184 *
1185 * @param[in,out] first_node First sibling of the data tree.
1186 * @param[in] parent_node Parent of the first sibling.
1187 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001188 * @param[in] diff_cb Optional diff callback.
1189 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001190 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001191 * @return LY_ERR value.
1192 */
1193static LY_ERR
1194lyd_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 +02001195 lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001196{
1197 LY_ERR ret;
1198 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001199 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001200 enum lyd_diff_op op;
1201 struct lyd_meta *meta;
Michal Vasko8efac242023-03-30 08:24:56 +02001202 struct ly_ht *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001203 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001204
1205 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001206 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001207
Michal Vaskoe6323f62020-07-09 15:49:02 +02001208 /* handle specific user-ordered (leaf-)lists operations separately */
1209 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1210 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001211 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001212 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001213 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001214 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001215 /* duplicate the node */
1216 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001217 }
1218
Michal Vaskoe78faec2021-04-08 17:24:43 +02001219 /* get "key", "value", or "position" metadata string value */
1220 if (lysc_is_dup_inst_list(diff_node->schema)) {
1221 meta_str = "yang:position";
1222 } else if (diff_node->schema->nodetype == LYS_LIST) {
1223 meta_str = "yang:key";
1224 } else {
1225 meta_str = "yang:value";
1226 }
1227 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001228 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001229 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001230
Michal Vaskod59035b2020-07-08 12:00:06 +02001231 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001232 if (str_val[0]) {
1233 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001234 } else {
1235 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1236 }
1237 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001238 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001239 lyd_free_tree(match);
1240 }
1241 return ret;
1242 }
1243
1244 goto next_iter_r;
1245 }
1246
1247 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001248 switch (op) {
1249 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001250 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001251 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001252 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001253
1254 if (match->schema->nodetype & LYD_NODE_TERM) {
1255 /* special case of only dflt flag change */
1256 if (diff_node->flags & LYD_DEFAULT) {
1257 match->flags |= LYD_DEFAULT;
1258 } else {
1259 match->flags &= ~LYD_DEFAULT;
1260 }
1261 } else {
1262 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001263 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001264 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1265 LYD_NAME(diff_node));
1266 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001267 }
1268 }
1269 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001270 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001271 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001272 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001273
1274 /* insert it at the end */
1275 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001276 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001277 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001278 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001279 } else {
1280 ret = lyd_insert_child(parent_node, match);
1281 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001282 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001283 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001284 }
1285 if (ret) {
1286 lyd_free_tree(match);
1287 return ret;
1288 }
1289
1290 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001291 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001292 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001293 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001294 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001295
1296 /* remove it */
1297 if ((match == *first_node) && !match->parent) {
1298 assert(!parent_node);
1299 /* we have removed the top-level node */
1300 *first_node = (*first_node)->next;
1301 }
1302 lyd_free_tree(match);
1303
1304 /* we are not going recursively in this case, the whole subtree was already deleted */
1305 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001306 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001307 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1308 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1309 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1310 return LY_EINVAL;
1311 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001312
1313 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001314 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001315 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001316
Michal Vaskobaba84e2021-02-05 16:33:30 +01001317 /* update the value */
1318 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001319 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001320 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001321 } else {
1322 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001323
Michal Vaskoe78faec2021-04-08 17:24:43 +02001324 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001325 }
1326
1327 /* with flags */
1328 match->flags = diff_node->flags;
1329 break;
1330 default:
1331 LOGINT_RET(ctx);
1332 }
1333
1334next_iter_r:
1335 if (diff_cb) {
1336 /* call callback */
1337 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1338 }
1339
1340 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001341 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001342 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001343 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1344 if (ret) {
1345 break;
1346 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001347 }
1348
Michal Vaskod7c048c2021-05-18 16:12:55 +02001349 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001350 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001351}
1352
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001353LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001354lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001355 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001356{
1357 const struct lyd_node *root;
Michal Vasko8efac242023-03-30 08:24:56 +02001358 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001359 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001360
1361 LY_LIST_FOR(diff, root) {
1362 if (mod && (lyd_owner_module(root) != mod)) {
1363 /* skip data nodes from different modules */
1364 continue;
1365 }
1366
1367 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001368 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1369 if (ret) {
1370 break;
1371 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001372 }
1373
Michal Vaskod7c048c2021-05-18 16:12:55 +02001374 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001375 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001376}
1377
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001378LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001379lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001380{
1381 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1382}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001383
1384/**
1385 * @brief Update operations on a diff node when the new operation is NONE.
1386 *
1387 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001388 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001389 * @param[in] src_diff Current source diff node.
1390 * @return LY_ERR value.
1391 */
1392static LY_ERR
1393lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1394{
1395 switch (cur_op) {
1396 case LYD_DIFF_OP_NONE:
1397 case LYD_DIFF_OP_CREATE:
1398 case LYD_DIFF_OP_REPLACE:
1399 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1400 /* NONE on a term means only its dflt flag was changed */
1401 diff_match->flags &= ~LYD_DEFAULT;
1402 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1403 }
1404 break;
1405 default:
1406 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001407 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1408 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001409 }
1410
1411 return LY_SUCCESS;
1412}
1413
1414/**
Michal Vaskoe6323f62020-07-09 15:49:02 +02001415 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001416 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001417 *
1418 * @param[in] node Node to change.
1419 * @param[in] op Operation to set.
1420 * @return LY_ERR value.
1421 */
1422static LY_ERR
1423lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1424{
Michal Vaskodb91fc32023-05-02 14:39:40 +02001425 lyd_diff_del_meta(node, "operation");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001426
Michal Vaskodb91fc32023-05-02 14:39:40 +02001427 if (node->schema) {
1428 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
1429 } else {
1430 return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001431 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001432}
1433
1434/**
1435 * @brief Update operations on a diff node when the new operation is REPLACE.
1436 *
1437 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001438 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001439 * @param[in] src_diff Current source diff node.
1440 * @return LY_ERR value.
1441 */
1442static LY_ERR
1443lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1444{
1445 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001446 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001447 struct lyd_meta *meta;
1448 const struct lys_module *mod;
1449 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001450 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001451
1452 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001453 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001454 assert(mod);
1455
1456 switch (cur_op) {
1457 case LYD_DIFF_OP_REPLACE:
1458 case LYD_DIFF_OP_CREATE:
1459 switch (diff_match->schema->nodetype) {
1460 case LYS_LIST:
1461 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001462 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001463 * keep orig_key/orig_value (only replace oper) and replace key/value */
1464 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001465 if (lysc_is_dup_inst_list(diff_match->schema)) {
1466 meta_name = "position";
1467 } else if (diff_match->schema->nodetype == LYS_LIST) {
1468 meta_name = "key";
1469 } else {
1470 meta_name = "value";
1471 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001472
1473 lyd_diff_del_meta(diff_match, meta_name);
1474 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001475 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001476 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001477 break;
1478 case LYS_LEAF:
1479 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001480 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001481 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1482 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001483 }
1484
Michal Vaskoe6323f62020-07-09 15:49:02 +02001485 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001486 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001487 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001488 }
1489
Michal Vasko8caadab2020-11-05 17:38:15 +01001490 if (cur_op == LYD_DIFF_OP_REPLACE) {
1491 /* compare values whether there is any change at all */
1492 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001493 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001494 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001495 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1496 if (!ret) {
1497 /* values are the same, remove orig-value meta and set oper to NONE */
1498 lyd_free_meta_single(meta);
1499 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1500 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001501 }
1502
1503 /* modify the default flag */
1504 diff_match->flags &= ~LYD_DEFAULT;
1505 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1506 break;
1507 case LYS_ANYXML:
1508 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001509 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001510 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1511 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001512 }
1513
1514 /* modify the node value */
1515 any = (struct lyd_node_any *)src_diff;
1516 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1517 break;
1518 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001519 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001520 }
1521 break;
1522 case LYD_DIFF_OP_NONE:
1523 /* it is moved now */
1524 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1525
1526 /* change the operation */
1527 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1528
Michal Vaskoe78faec2021-04-08 17:24:43 +02001529 /* set orig-meta and meta */
1530 if (lysc_is_dup_inst_list(diff_match->schema)) {
1531 meta_name = "position";
1532 orig_meta_name = "orig-position";
1533 } else {
1534 meta_name = "key";
1535 orig_meta_name = "orig-key";
1536 }
1537
1538 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001539 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001540 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001541
Michal Vaskoe78faec2021-04-08 17:24:43 +02001542 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001543 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001544 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001545 break;
1546 default:
1547 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001548 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1549 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001550 }
1551
1552 return LY_SUCCESS;
1553}
1554
1555/**
1556 * @brief Update operations in a diff node when the new operation is CREATE.
1557 *
1558 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001559 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001560 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001561 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001562 * @return LY_ERR value.
1563 */
1564static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001565lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001566{
1567 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001568 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001569 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001570 const char *meta_name, *orig_meta_name;
1571 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001572 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001573
1574 switch (cur_op) {
1575 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001576 /* remember current flags */
1577 trg_flags = diff_match->flags;
1578
Michal Vaskoe78faec2021-04-08 17:24:43 +02001579 if (lysc_is_userordered(diff_match->schema)) {
1580 /* get anchor metadata */
1581 if (lysc_is_dup_inst_list(diff_match->schema)) {
1582 meta_name = "yang:position";
1583 orig_meta_name = "yang:orig-position";
1584 } else if (diff_match->schema->nodetype == LYS_LIST) {
1585 meta_name = "yang:key";
1586 orig_meta_name = "yang:orig-key";
1587 } else {
1588 meta_name = "yang:value";
1589 orig_meta_name = "yang:orig-value";
1590 }
1591 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001592 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001593 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001594 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001595
1596 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1597 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001598 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001599 /* deleted + created at another position -> operation REPLACE */
1600 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1601
1602 /* add anchor metadata */
1603 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1604 } else {
1605 /* deleted + created at the same position -> operation NONE */
1606 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1607
1608 /* delete anchor metadata */
1609 lyd_free_meta_single(orig_meta);
1610 }
1611 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1612 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1613 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1614 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1615 }
1616
Radek Krejci55c4bd22021-04-26 08:09:04 +02001617 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1618 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001619 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1620 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1621 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1622 /* deleted + created -> operation NONE */
1623 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1624 } else {
1625 /* we deleted it, but it was created with a different value -> operation REPLACE */
1626 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1627
1628 /* current value is the previous one (meta) */
1629 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001630 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001631
1632 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001633 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001634 }
1635 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001636 /* deleted + created -> operation NONE */
1637 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001638 }
1639
1640 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001641 /* add orig-dflt metadata */
1642 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1643 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1644
Michal Vaskoe6323f62020-07-09 15:49:02 +02001645 /* update dflt flag itself */
1646 diff_match->flags &= ~LYD_DEFAULT;
1647 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001648 }
1649
1650 /* but the operation of its children should remain DELETE */
1651 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1652 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001653 }
1654 break;
1655 default:
1656 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001657 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1658 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001659 }
1660
1661 return LY_SUCCESS;
1662}
1663
1664/**
1665 * @brief Update operations on a diff node when the new operation is DELETE.
1666 *
1667 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001668 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001669 * @param[in] src_diff Current source diff node.
1670 * @return LY_ERR value.
1671 */
1672static LY_ERR
1673lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1674{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001675 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001676 struct lyd_meta *meta;
1677 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001678 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001679
1680 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001681 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 +02001682
1683 switch (cur_op) {
1684 case LYD_DIFF_OP_CREATE:
1685 /* it was created, but then deleted -> set NONE operation */
1686 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1687
1688 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1689 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001690 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1691 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001692 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001693 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001694 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001695 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1696 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001697 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001698 break;
1699 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001700 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001701 if (lysc_is_userordered(diff_match->schema)) {
1702 if (lysc_is_dup_inst_list(diff_match->schema)) {
1703 meta_name = "position";
1704 } else if (diff_match->schema->nodetype == LYS_LIST) {
1705 meta_name = "key";
1706 } else {
1707 meta_name = "value";
1708 }
1709 } else {
1710 assert(diff_match->schema->nodetype == LYS_LEAF);
1711
1712 /* switch value for the original one */
1713 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001714 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001715 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001716 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1717 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001718 }
1719
1720 /* switch default for the original one, then remove the meta */
1721 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001722 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001723 diff_match->flags &= ~LYD_DEFAULT;
1724 if (meta->value.boolean) {
1725 diff_match->flags |= LYD_DEFAULT;
1726 }
1727 lyd_free_meta_single(meta);
1728
1729 meta_name = "orig-value";
1730 }
1731 lyd_diff_del_meta(diff_match, meta_name);
1732
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001733 /* it was being changed, but should be deleted instead -> set DELETE operation */
1734 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1735 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001736 case LYD_DIFF_OP_NONE:
1737 /* it was not modified, but should be deleted -> set DELETE operation */
1738 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001739 break;
1740 default:
1741 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001742 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1743 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001744 }
1745
1746 return LY_SUCCESS;
1747}
1748
1749/**
1750 * @brief Check whether this diff node is redundant (does not change data).
1751 *
1752 * @param[in] diff Diff node.
1753 * @return 0 if not, non-zero if it is.
1754 */
1755static int
1756lyd_diff_is_redundant(struct lyd_node *diff)
1757{
1758 enum lyd_diff_op op;
1759 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1760 struct lyd_node *child;
1761 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001762 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001763
1764 assert(diff);
1765
Michal Vaskoe78faec2021-04-08 17:24:43 +02001766 if (lysc_is_dup_inst_list(diff->schema)) {
1767 /* all descendants are keys */
1768 child = NULL;
1769 } else {
1770 child = lyd_child_no_keys(diff);
1771 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001772 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001773 assert(mod);
1774
1775 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001776 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001777
1778 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001779 /* get metadata names */
1780 if (lysc_is_dup_inst_list(diff->schema)) {
1781 meta_name = "position";
1782 orig_meta_name = "orig-position";
1783 } else if (diff->schema->nodetype == LYS_LIST) {
1784 meta_name = "key";
1785 orig_meta_name = "orig-key";
1786 } else {
1787 meta_name = "value";
1788 orig_meta_name = "orig-value";
1789 }
1790
Michal Vaskoe6323f62020-07-09 15:49:02 +02001791 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001792 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1793 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001794 assert(orig_val_meta && val_meta);
1795
1796 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1797 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001798 lyd_free_meta_single(orig_val_meta);
1799 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001800 if (child) {
1801 /* change operation to NONE, we have siblings */
1802 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1803 return 0;
1804 }
1805
1806 /* redundant node, BUT !!
1807 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1808 * because the data that this is applied on should not change for the diff lifetime.
1809 * However, when we are merging 2 diffs, this conversion is actually lossy because
1810 * if the data change, the move operation can also change its meaning. In this specific
1811 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1812 */
1813 return 1;
1814 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001815 } else if (op == LYD_DIFF_OP_NONE) {
1816 if (!diff->schema) {
1817 /* opaque node with none must be redundant */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001818 return 1;
1819 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001820
1821 if (diff->schema->nodetype & LYD_NODE_TERM) {
1822 /* check whether at least the default flags are different */
1823 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1824 assert(meta);
1825 str = lyd_get_meta_value(meta);
1826
1827 /* if previous and current dflt flags are the same, this node is redundant */
1828 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1829 return 1;
1830 }
1831 return 0;
1832 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001833 }
1834
1835 if (!child && (op == LYD_DIFF_OP_NONE)) {
1836 return 1;
1837 }
1838
1839 return 0;
1840}
1841
1842/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001843 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001844 *
1845 * @param[in] src_diff Source diff node.
1846 * @param[in] diff_parent Current sysrepo diff parent.
1847 * @param[in] diff_cb Optional diff callback.
1848 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001849 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001850 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001851 * @param[in,out] diff Diff root node.
1852 * @return LY_ERR value.
1853 */
1854static LY_ERR
1855lyd_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 +02001856 struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001857{
1858 LY_ERR ret = LY_SUCCESS;
1859 struct lyd_node *child, *diff_node = NULL;
1860 enum lyd_diff_op src_op, cur_op;
Michal Vasko8efac242023-03-30 08:24:56 +02001861 struct ly_ht *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001862
1863 /* get source node operation */
1864 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1865
1866 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001867 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 +02001868
1869 if (diff_node) {
1870 /* get target (current) operation */
1871 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1872
1873 /* merge operations */
1874 switch (src_op) {
1875 case LYD_DIFF_OP_REPLACE:
1876 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1877 break;
1878 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001879 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001880 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001881 goto add_diff;
1882 }
1883
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001884 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001885 break;
1886 case LYD_DIFF_OP_DELETE:
1887 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1888 break;
1889 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001890 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1891 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001892 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1893 break;
1894 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001895 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001896 }
1897 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001898 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001899 return ret;
1900 }
1901
1902 if (diff_cb) {
1903 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001904 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001905 }
1906
1907 /* update diff parent */
1908 diff_parent = diff_node;
1909
Michal Vaskoe78faec2021-04-08 17:24:43 +02001910 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1911 * so there is nothing to merge for these "keys" */
1912 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1913 /* merge src_diff recursively */
1914 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1915 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1916 if (ret) {
1917 break;
1918 }
1919 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001920 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001921 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001922 }
1923 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001924add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001925 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001926 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1927 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1928 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1929 } else {
1930 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1931 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1932 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001933
1934 /* insert node into diff if not already */
1935 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001936 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001937 }
1938
1939 /* update operation */
1940 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1941
1942 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001943 /* call callback with no source diff node since it was duplicated and just added */
1944 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001945 }
1946
1947 /* update diff parent */
1948 diff_parent = diff_node;
1949 }
1950
1951 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001952 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001953 if (diff_parent == *diff) {
1954 *diff = (*diff)->next;
1955 }
1956 lyd_free_tree(diff_parent);
1957 }
1958
1959 return LY_SUCCESS;
1960}
1961
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001962LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001963lyd_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 +01001964 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001965{
1966 const struct lyd_node *src_root;
Michal Vasko8efac242023-03-30 08:24:56 +02001967 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001968 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001969
1970 LY_LIST_FOR(src_diff, src_root) {
1971 if (mod && (lyd_owner_module(src_root) != mod)) {
1972 /* skip data nodes from different modules */
1973 continue;
1974 }
1975
1976 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001977 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 +02001978 }
1979
Michal Vaskoe78faec2021-04-08 17:24:43 +02001980cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001981 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001982 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001983}
1984
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001985LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001986lyd_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 +01001987 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001988{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001989 LY_ERR ret;
Michal Vasko8efac242023-03-30 08:24:56 +02001990 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001991
Michal Vasko04f85912020-08-07 12:14:58 +02001992 if (!src_sibling) {
1993 return LY_SUCCESS;
1994 }
1995
Michal Vaskoe78faec2021-04-08 17:24:43 +02001996 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 +02001997 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001998 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001999}
2000
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002001LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002002lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02002003{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002004 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002005}
Michal Vasko4231fb62020-07-13 13:54:47 +02002006
2007static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01002008lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02002009{
2010 LY_ERR ret = LY_SUCCESS;
2011 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002012 const char *val1 = NULL;
2013 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002014 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002015
Michal Vaskobaba84e2021-02-05 16:33:30 +01002016 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
2017
2018 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01002019 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002020
2021 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002022 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02002023
2024 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002025 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002026 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01002027 } else {
2028 LY_CHECK_RET(lyd_any_value_str(node, &val2));
2029 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002030
2031 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002032 flags = node->flags;
2033 if (node->schema->nodetype == LYS_LEAF) {
2034 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
2035 } else {
2036 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02002037
Michal Vaskobaba84e2021-02-05 16:33:30 +01002038 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
2039 }
2040 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002041 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
2042
2043cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002044 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002045 return ret;
2046}
2047
2048static LY_ERR
2049lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
2050{
2051 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002052 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02002053
2054 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01002055 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02002056
2057 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002058 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02002059 flag1 = LYD_DEFAULT;
2060 } else {
2061 flag1 = 0;
2062 }
2063
2064 /* current default */
2065 flag2 = node->flags & LYD_DEFAULT;
2066
Michal Vasko610e93b2020-11-09 20:58:32 +01002067 if (flag1 == flag2) {
2068 /* no default state change so nothing to reverse */
2069 return LY_SUCCESS;
2070 }
2071
Michal Vasko4231fb62020-07-13 13:54:47 +02002072 /* switch defaults */
2073 node->flags &= ~LYD_DEFAULT;
2074 node->flags |= flag1;
2075 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
2076
2077 return LY_SUCCESS;
2078}
2079
2080static LY_ERR
2081lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
2082{
2083 LY_ERR ret = LY_SUCCESS;
2084 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002085 const char *val1 = NULL;
2086 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02002087
2088 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002089 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002090
2091 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002092 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002093
2094 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002095 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02002096
2097 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002098 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002099
2100 /* switch values */
2101 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2102 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2103
2104cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002105 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002106 return ret;
2107}
2108
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002109/**
2110 * @brief Remove specific operation from all the nodes in a subtree.
2111 *
2112 * @param[in] diff Diff subtree to process.
2113 * @param[in] op Only expected operation.
2114 * @return LY_ERR value.
2115 */
2116static LY_ERR
2117lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2118{
2119 struct lyd_node *elem;
2120 struct lyd_meta *meta;
2121
2122 LYD_TREE_DFS_BEGIN(diff, elem) {
2123 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2124 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002125 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 +01002126 lyd_free_meta_single(meta);
2127 }
2128
2129 LYD_TREE_DFS_END(diff, elem);
2130 }
2131
2132 return LY_SUCCESS;
2133}
2134
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002135LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002136lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002137{
2138 LY_ERR ret = LY_SUCCESS;
2139 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002140 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002141 enum lyd_diff_op op;
2142
2143 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2144
2145 if (!src_diff) {
2146 *diff = NULL;
2147 return LY_SUCCESS;
2148 }
2149
2150 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002151 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002152
2153 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002154 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2155 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002156
2157 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002158 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002159 /* skip all keys */
2160 if (!lysc_is_key(elem->schema)) {
2161 /* find operation attribute, if any */
2162 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002163
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002164 switch (op) {
2165 case LYD_DIFF_OP_CREATE:
2166 /* reverse create to delete */
2167 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002168
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002169 /* check all the children for the same operation, nothing else is expected */
2170 LY_LIST_FOR(lyd_child(elem), iter) {
2171 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2172 }
2173
Michal Vasko9e070522021-03-05 14:00:14 +01002174 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002175 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002176 case LYD_DIFF_OP_DELETE:
2177 /* reverse delete to create */
2178 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002179
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002180 /* check all the children for the same operation, nothing else is expected */
2181 LY_LIST_FOR(lyd_child(elem), iter) {
2182 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2183 }
2184
Michal Vasko9e070522021-03-05 14:00:14 +01002185 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002186 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002187 case LYD_DIFF_OP_REPLACE:
2188 switch (elem->schema->nodetype) {
2189 case LYS_LEAF:
2190 /* leaf value change */
2191 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2192 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2193 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002194 case LYS_ANYXML:
2195 case LYS_ANYDATA:
2196 /* any value change */
2197 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2198 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002199 case LYS_LEAFLIST:
2200 /* leaf-list move */
2201 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002202 if (lysc_is_dup_inst_list(elem->schema)) {
2203 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2204 } else {
2205 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2206 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002207 break;
2208 case LYS_LIST:
2209 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002210 if (lysc_is_dup_inst_list(elem->schema)) {
2211 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2212 } else {
2213 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2214 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002215 break;
2216 default:
2217 LOGINT(LYD_CTX(src_diff));
2218 ret = LY_EINT;
2219 goto cleanup;
2220 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002221 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002222 case LYD_DIFF_OP_NONE:
2223 switch (elem->schema->nodetype) {
2224 case LYS_LEAF:
2225 case LYS_LEAFLIST:
2226 /* default flag change */
2227 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2228 break;
2229 default:
2230 /* nothing to do */
2231 break;
2232 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002233 break;
2234 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002235 }
2236
Michal Vasko56daf732020-08-10 10:57:18 +02002237 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002238 }
2239 }
2240
2241cleanup:
2242 if (ret) {
2243 lyd_free_siblings(*diff);
2244 *diff = NULL;
2245 }
2246 return ret;
2247}