summaryrefslogtreecommitdiffstats
path: root/src/diff.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/diff.c488
1 files changed, 318 insertions, 170 deletions
diff --git a/src/diff.c b/src/diff.c
index edfcb34..f30d44a 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -161,6 +161,127 @@ cleanup:
return rc;
}
+/**
+ * @brief Find metadata/an attribute of a node.
+ *
+ * @param[in] node Node to search.
+ * @param[in] name Metadata/attribute name.
+ * @param[out] meta Metadata found, NULL if not found.
+ * @param[out] attr Attribute found, NULL if not found.
+ */
+static void
+lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
+{
+ struct lyd_meta *m;
+ struct lyd_attr *a;
+
+ if (meta) {
+ *meta = NULL;
+ }
+ if (attr) {
+ *attr = NULL;
+ }
+
+ if (node->schema) {
+ assert(meta);
+
+ LY_LIST_FOR(node->meta, m) {
+ if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
+ *meta = m;
+ break;
+ }
+ }
+ } else {
+ assert(attr);
+
+ LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
+ /* name */
+ if (strcmp(a->name.name, name)) {
+ continue;
+ }
+
+ /* module */
+ switch (a->format) {
+ case LY_VALUE_JSON:
+ if (strcmp(a->name.module_name, "yang")) {
+ continue;
+ }
+ break;
+ case LY_VALUE_XML:
+ if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
+ continue;
+ }
+ break;
+ default:
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+
+ *attr = a;
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Learn operation of a diff node.
+ *
+ * @param[in] diff_node Diff node.
+ * @param[out] op Operation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
+{
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lyd_node *diff_parent;
+ const char *str;
+ char *path;
+
+ for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
+ lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
+ if (!meta && !attr) {
+ continue;
+ }
+
+ str = meta ? lyd_get_meta_value(meta) : attr->value;
+ if ((str[0] == 'r') && (diff_parent != diff_node)) {
+ /* we do not care about this operation if it's in our parent */
+ continue;
+ }
+ *op = lyd_diff_str2op(str);
+ return LY_SUCCESS;
+ }
+
+ /* operation not found */
+ path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
+ LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
+ free(path);
+ return LY_EINT;
+}
+
+/**
+ * @brief Remove metadata/an attribute from a node.
+ *
+ * @param[in] node Node to update.
+ * @param[in] name Metadata/attribute name.
+ */
+static void
+lyd_diff_del_meta(struct lyd_node *node, const char *name)
+{
+ struct lyd_meta *meta;
+ struct lyd_attr *attr;
+
+ lyd_diff_find_meta(node, name, &meta, &attr);
+
+ if (meta) {
+ lyd_free_meta_single(meta);
+ } else if (attr) {
+ lyd_free_attr_single(LYD_CTX(node), attr);
+ }
+}
+
LY_ERR
lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
@@ -168,6 +289,9 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
{
struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
const struct lyd_node *parent = NULL;
+ enum lyd_diff_op cur_op;
+ struct lyd_meta *meta;
+ uint32_t diff_opts;
assert(diff);
@@ -189,14 +313,16 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
/* find the first existing parent */
siblings = *diff;
- while (1) {
+ do {
/* find next node parent */
parent = node;
while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
parent = lyd_parent(parent);
}
- if (parent == node) {
- /* no more parents to find */
+
+ if (lysc_is_dup_inst_list(parent->schema)) {
+ /* assume it never exists, we are not able to distinguish whether it does or not */
+ match = NULL;
break;
}
@@ -210,36 +336,75 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
/* move down in the diff */
siblings = lyd_child_no_keys(match);
- }
+ } while (parent != node);
+
+ if (match && (parent == node)) {
+ /* special case when there is already an operation on our descendant */
+ assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
+ (void)cur_op;
+
+ /* move it to the end where it is expected (matters for user-ordered lists) */
+ if (lysc_is_userordered(diff_parent->schema)) {
+ for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {}
+ if (elem != diff_parent) {
+ LY_CHECK_RET(lyd_insert_after(elem, diff_parent));
+ }
+ }
- /* duplicate the subtree (and connect to the diff if possible) */
- LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
- LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
+ /* will be replaced by the new operation but keep the current op for descendants */
+ lyd_diff_del_meta(diff_parent, "operation");
+ LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
+ lyd_diff_find_meta(elem, "operation", &meta, NULL);
+ if (meta) {
+ /* explicit operation, fine */
+ continue;
+ }
- /* find the first duplicated parent */
- if (!diff_parent) {
- diff_parent = lyd_parent(dup);
- while (diff_parent && diff_parent->parent) {
- diff_parent = lyd_parent(diff_parent);
+ /* set the none operation */
+ LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", 0, NULL));
}
+
+ dup = diff_parent;
} else {
- diff_parent = dup;
- while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
- diff_parent = lyd_parent(diff_parent);
+ diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS;
+ if ((op != LYD_DIFF_OP_REPLACE) || !lysc_is_userordered(node->schema) || (node->schema->flags & LYS_CONFIG_R)) {
+ /* move applies only to the user-ordered list, no descendants */
+ diff_opts |= LYD_DUP_RECURSIVE;
}
- }
- /* no parent existed, must be manually connected */
- if (!diff_parent) {
- /* there actually was no parent to duplicate */
- lyd_insert_sibling(*diff, dup, diff);
- } else if (!diff_parent->parent) {
- lyd_insert_sibling(*diff, diff_parent, diff);
- }
+ /* duplicate the subtree (and connect to the diff if possible) */
+ if (diff_parent) {
+ LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
+ diff_opts, &dup));
+ } else {
+ LY_CHECK_RET(lyd_dup_single(node, NULL, diff_opts, &dup));
+ }
+
+ /* find the first duplicated parent */
+ if (!diff_parent) {
+ diff_parent = lyd_parent(dup);
+ while (diff_parent && diff_parent->parent) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ } else {
+ diff_parent = dup;
+ while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ }
+
+ /* no parent existed, must be manually connected */
+ if (!diff_parent) {
+ /* there actually was no parent to duplicate */
+ lyd_insert_sibling(*diff, dup, diff);
+ } else if (!diff_parent->parent) {
+ lyd_insert_sibling(*diff, diff_parent, diff);
+ }
- /* add parent operation, if any */
- if (diff_parent && (diff_parent != dup)) {
- LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ /* add parent operation, if any */
+ if (diff_parent && (diff_parent != dup)) {
+ LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ }
}
/* add subtree operation */
@@ -361,7 +526,7 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco
LY_ERR rc = LY_SUCCESS;
const struct lysc_node *schema;
size_t buflen, bufused;
- uint32_t first_pos, second_pos;
+ uint32_t first_pos, second_pos, comp_opts;
assert(first || second);
@@ -397,7 +562,8 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco
} else if (!first) {
*op = LYD_DIFF_OP_CREATE;
} else {
- if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
+ comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
+ if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
/* in first, there is a different instance on the second position, we are going to move 'first' node */
*op = LYD_DIFF_OP_REPLACE;
} else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
@@ -648,17 +814,20 @@ lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint
* @param[in] siblings Siblings to search in.
* @param[in] target Target node to search for.
* @param[in] defaults Whether to consider (or ignore) default values.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in,out] dup_inst_ht Duplicate instance cache.
* @param[out] match Found match, NULL if no matching node found.
* @return LY_ERR value.
*/
static LY_ERR
lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
- struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
+ struct ly_ht **dup_inst_ht, struct lyd_node **match)
{
LY_ERR r;
- if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ if (!target->schema) {
+ /* try to find the same opaque node */
+ r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
+ } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
/* try to find the exact instance */
r = lyd_find_sibling_first(siblings, target, match);
} else {
@@ -670,7 +839,7 @@ lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *targ
}
/* update match as needed */
- LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
+ LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
/* ignore default nodes */
@@ -728,7 +897,7 @@ lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second,
const struct lyd_node *iter_first, *iter_second;
struct lyd_node *match_second, *match_first;
struct lyd_diff_userord *userord = NULL, *userord_item;
- struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
+ struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
LY_ARRAY_COUNT_TYPE u;
enum lyd_diff_op op;
const char *orig_default;
@@ -920,48 +1089,6 @@ lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, u
}
/**
- * @brief Learn operation of a diff node.
- *
- * @param[in] diff_node Diff node.
- * @param[out] op Operation.
- * @return LY_ERR value.
- */
-static LY_ERR
-lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
-{
- struct lyd_meta *meta = NULL;
- const struct lyd_node *diff_parent;
- const char *str;
- char *path;
-
- for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
- LY_LIST_FOR(diff_parent->meta, meta) {
- if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
- str = lyd_get_meta_value(meta);
- if ((str[0] == 'r') && (diff_parent != diff_node)) {
- /* we do not care about this operation if it's in our parent */
- continue;
- }
- *op = lyd_diff_str2op(str);
- break;
- }
- }
- if (meta) {
- break;
- }
- }
-
- if (!meta) {
- path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
- LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
- free(path);
- return LY_EINT;
- }
-
- return LY_SUCCESS;
-}
-
-/**
* @brief Insert a diff node into a data tree.
*
* @param[in,out] first_node First sibling of the data tree.
@@ -1079,14 +1206,14 @@ lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, stru
*/
static LY_ERR
lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
- lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
+ lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
{
LY_ERR ret;
struct lyd_node *match, *diff_child;
const char *str_val, *meta_str;
enum lyd_diff_op op;
struct lyd_meta *meta;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
const struct ly_ctx *ctx = LYD_CTX(diff_node);
/* read all the valid attributes */
@@ -1242,7 +1369,7 @@ lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const
lyd_diff_cb diff_cb, void *cb_data)
{
const struct lyd_node *root;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
LY_ERR ret = LY_SUCCESS;
LY_LIST_FOR(diff, root) {
@@ -1299,27 +1426,6 @@ lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const
}
/**
- * @brief Remove an attribute from a node.
- *
- * @param[in] node Node with the metadata.
- * @param[in] name Metadata name.
- */
-static void
-lyd_diff_del_meta(struct lyd_node *node, const char *name)
-{
- struct lyd_meta *meta;
-
- LY_LIST_FOR(node->meta, meta) {
- if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
- lyd_free_meta_single(meta);
- return;
- }
- }
-
- assert(0);
-}
-
-/**
* @brief Set a specific operation of a node. Delete the previous operation, if any.
* Does not change the default flag.
*
@@ -1330,16 +1436,13 @@ lyd_diff_del_meta(struct lyd_node *node, const char *name)
static LY_ERR
lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
{
- struct lyd_meta *meta;
+ lyd_diff_del_meta(node, "operation");
- LY_LIST_FOR(node->meta, meta) {
- if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
- lyd_free_meta_single(meta);
- break;
- }
+ if (node->schema) {
+ return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
+ } else {
+ return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
}
-
- return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
}
/**
@@ -1431,28 +1534,43 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
}
break;
case LYD_DIFF_OP_NONE:
- /* it is moved now */
- assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
+ switch (diff_match->schema->nodetype) {
+ case LYS_LIST:
+ /* it is moved now */
+ assert(lysc_is_userordered(diff_match->schema));
- /* change the operation */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ /* change the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
- /* set orig-meta and meta */
- if (lysc_is_dup_inst_list(diff_match->schema)) {
- meta_name = "position";
- orig_meta_name = "orig-position";
- } else {
- meta_name = "key";
- orig_meta_name = "orig-key";
- }
+ /* set orig-meta and meta */
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "position";
+ orig_meta_name = "orig-position";
+ } else {
+ meta_name = "key";
+ orig_meta_name = "orig-key";
+ }
+
+ meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
- meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
- LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ meta = lyd_find_meta(src_diff->meta, mod, meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ break;
+ case LYS_LEAF:
+ /* only dflt flag changed, now value changed as well, update the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
- meta = lyd_find_meta(src_diff->meta, mod, meta_name);
- LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ /* modify the node value */
+ if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
+ break;
+ default:
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
break;
default:
/* delete operation is not valid */
@@ -1466,33 +1584,41 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
/**
* @brief Update operations in a diff node when the new operation is CREATE.
*
- * @param[in] diff_match Node from the diff.
+ * @param[in,out] diff_match Node from the diff, may be replaced.
+ * @param[in,out] diff Diff root node, may be updated.
* @param[in] cur_op Current operation of @p diff_match.
* @param[in] src_diff Current source diff node.
* @param[in] options Diff merge options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
+lyd_diff_merge_create(struct lyd_node **diff_match, struct lyd_node **diff, enum lyd_diff_op cur_op,
+ const struct lyd_node *src_diff, uint16_t options)
{
- struct lyd_node *child;
+ struct lyd_node *child, *src_dup, *to_free = NULL;
const struct lysc_node_leaf *sleaf = NULL;
uint32_t trg_flags;
const char *meta_name, *orig_meta_name;
struct lyd_meta *meta, *orig_meta;
- const struct ly_ctx *ctx = LYD_CTX(diff_match);
+ const struct ly_ctx *ctx = LYD_CTX(*diff_match);
+ LY_ERR r;
+
+ /* create operation is valid only for data nodes */
+ LY_CHECK_ERR_RET(!src_diff->schema, LOGINT(ctx), LY_EINT);
switch (cur_op) {
case LYD_DIFF_OP_DELETE:
/* remember current flags */
- trg_flags = diff_match->flags;
+ trg_flags = (*diff_match)->flags;
+
+ if (lysc_is_userordered(src_diff->schema)) {
+ assert((*diff_match)->schema);
- if (lysc_is_userordered(diff_match->schema)) {
/* get anchor metadata */
- if (lysc_is_dup_inst_list(diff_match->schema)) {
+ if (lysc_is_dup_inst_list((*diff_match)->schema)) {
meta_name = "yang:position";
orig_meta_name = "yang:orig-position";
- } else if (diff_match->schema->nodetype == LYS_LIST) {
+ } else if ((*diff_match)->schema->nodetype == LYS_LIST) {
meta_name = "yang:key";
orig_meta_name = "yang:orig-key";
} else {
@@ -1501,71 +1627,86 @@ lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons
}
meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
- orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
- LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
+ orig_meta = lyd_find_meta((*diff_match)->meta, NULL, orig_meta_name);
+ LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, *diff_match), LY_EINVAL);
/* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
* the anchors stored in the metadata */
if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
/* deleted + created at another position -> operation REPLACE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
/* add anchor metadata */
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ LY_CHECK_RET(lyd_dup_meta_single(meta, *diff_match, NULL));
} else {
/* deleted + created at the same position -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
/* delete anchor metadata */
lyd_free_meta_single(orig_meta);
}
- } else if (diff_match->schema->nodetype == LYS_LEAF) {
+ } else if (src_diff->schema->nodetype == LYS_LEAF) {
if (options & LYD_DIFF_MERGE_DEFAULTS) {
/* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
- sleaf = (struct lysc_node_leaf *)diff_match->schema;
+ sleaf = (struct lysc_node_leaf *)src_diff->schema;
}
if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
&((struct lyd_node_term *)src_diff)->value)) {
/* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
- } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
+ } else if (!lyd_compare_single(*diff_match, src_diff, 0)) {
/* deleted + created -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
- } else {
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
+ } else if ((*diff_match)->schema) {
/* we deleted it, but it was created with a different value -> operation REPLACE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
/* current value is the previous one (meta) */
- LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
- lyd_get_value(diff_match), 0, NULL));
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-value",
+ lyd_get_value(*diff_match), 0, NULL));
/* update the value itself */
- LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
+ LY_CHECK_RET(lyd_change_term(*diff_match, lyd_get_value(src_diff)));
+ } else {
+ /* also operation REPLACE but we need to change an opaque node into a data node */
+ LY_CHECK_RET(lyd_dup_single(src_diff, (*diff_match)->parent, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &src_dup));
+ if (!(*diff_match)->parent) {
+ /* will always be inserted before diff_match, which is opaque */
+ LY_CHECK_RET(lyd_insert_sibling(*diff_match, src_dup, diff));
+ }
+ to_free = *diff_match;
+ *diff_match = src_dup;
+
+ r = lyd_new_meta(ctx, src_dup, NULL, "yang:orig-value", lyd_get_value(to_free), 0, NULL);
+ lyd_free_tree(to_free);
+ LY_CHECK_RET(r);
+ LY_CHECK_RET(lyd_new_meta(ctx, src_dup, NULL, "yang:operation", lyd_diff_op2str(LYD_DIFF_OP_REPLACE), 0, NULL));
}
} else {
/* deleted + created -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
}
- if (diff_match->schema->nodetype & LYD_NODE_TERM) {
+ assert((*diff_match)->schema);
+ if ((*diff_match)->schema->nodetype & LYD_NODE_TERM) {
/* add orig-dflt metadata */
- LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-default",
trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
/* update dflt flag itself */
- diff_match->flags &= ~LYD_DEFAULT;
- diff_match->flags |= src_diff->flags & LYD_DEFAULT;
+ (*diff_match)->flags &= ~LYD_DEFAULT;
+ (*diff_match)->flags |= src_diff->flags & LYD_DEFAULT;
}
/* but the operation of its children should remain DELETE */
- LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
+ LY_LIST_FOR(lyd_child_no_keys(*diff_match), child) {
LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
}
break;
default:
/* create and replace operations are not valid */
- LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
+ LOGERR_MERGEOP(LYD_CTX(src_diff), *diff_match, cur_op, LYD_DIFF_OP_CREATE);
return LY_EINVAL;
}
@@ -1599,7 +1740,7 @@ lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons
if (diff_match->schema->nodetype & LYD_NODE_TERM) {
/* add orig-default meta because it is expected */
LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
- diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
+ src_diff->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
} else if (!lysc_is_dup_inst_list(diff_match->schema)) {
/* keep operation for all descendants (for now) */
LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
@@ -1723,17 +1864,24 @@ lyd_diff_is_redundant(struct lyd_node *diff)
*/
return 1;
}
- } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
- /* check whether at least the default flags are different */
- meta = lyd_find_meta(diff->meta, mod, "orig-default");
- assert(meta);
- str = lyd_get_meta_value(meta);
-
- /* if previous and current dflt flags are the same, this node is redundant */
- if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ } else if (op == LYD_DIFF_OP_NONE) {
+ if (!diff->schema) {
+ /* opaque node with none must be redundant */
return 1;
}
- return 0;
+
+ if (diff->schema->nodetype & LYD_NODE_TERM) {
+ /* check whether at least the default flags are different */
+ meta = lyd_find_meta(diff->meta, mod, "orig-default");
+ assert(meta);
+ str = lyd_get_meta_value(meta);
+
+ /* if previous and current dflt flags are the same, this node is redundant */
+ if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ return 1;
+ }
+ return 0;
+ }
}
if (!child && (op == LYD_DIFF_OP_NONE)) {
@@ -1757,12 +1905,12 @@ lyd_diff_is_redundant(struct lyd_node *diff)
*/
static LY_ERR
lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
- struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
+ struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *child, *diff_node = NULL;
enum lyd_diff_op src_op, cur_op;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
/* get source node operation */
LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
@@ -1785,7 +1933,7 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent,
goto add_diff;
}
- ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
+ ret = lyd_diff_merge_create(&diff_node, diff, cur_op, src_diff, options);
break;
case LYD_DIFF_OP_DELETE:
ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
@@ -1868,7 +2016,7 @@ lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, c
lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
{
const struct lyd_node *src_root;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
LY_ERR ret = LY_SUCCESS;
LY_LIST_FOR(src_diff, src_root) {
@@ -1891,7 +2039,7 @@ lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent,
lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
{
LY_ERR ret;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
if (!src_sibling) {
return LY_SUCCESS;