diff options
Diffstat (limited to '')
-rw-r--r-- | src/diff.c | 488 |
1 files changed, 318 insertions, 170 deletions
@@ -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; |