@@ -2004,6 +2004,86 @@ lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
20042004 }
20052005}
20062006
2007+ /**
2008+ * @brief In user-ordered lists, certain operations on sibling nodes can result in logically identical changes.
2009+ * However, applying the first change may cause the second one to fail.
2010+ * To prevent this, the affected node is unlinked and freed.
2011+ *
2012+ * @param[in,out] diff The node whose metadata has been modified.
2013+ * @param[in] mod The YANG module associated with the metadata.
2014+ * @return LY_ERR value.
2015+ */
2016+ static LY_ERR
2017+ lyd_diff_find_and_unlink_cyclic_nodes (struct lyd_node * * diff , const struct lys_module * mod )
2018+ {
2019+ LY_ERR ret = LY_SUCCESS ;
2020+ struct lyd_meta * meta1 , * meta2 ;
2021+ struct lyd_node * diff_iter = * diff ;
2022+ char * buff1 = NULL , * buff2 = NULL ;
2023+ const char * name = NULL , * name_iter = NULL ;
2024+ size_t bufflen1 = 0 , buffused1 = 0 ;
2025+ size_t bufflen2 = 0 , buffused2 = 0 ;
2026+
2027+ /* itereate throught previous nodes and look for logically identical changes */
2028+ while (diff_iter -> prev -> next ) {
2029+ diff_iter = diff_iter -> prev ;
2030+
2031+ meta1 = lyd_find_meta ((* diff )-> meta , mod , "key" );
2032+ meta2 = lyd_find_meta (diff_iter -> meta , mod , "orig-key" );
2033+
2034+ name = lyd_get_meta_value (meta1 );
2035+ name_iter = lyd_get_meta_value (meta2 );
2036+
2037+ if (!name || !name_iter ) {
2038+ continue ;
2039+ }
2040+
2041+ /* if keys don't match, skip - not a candidate for cyclic change */
2042+ if (strcmp (name , name_iter )) {
2043+ continue ;
2044+ }
2045+
2046+ meta1 = lyd_find_meta ((* diff )-> meta , mod , "orig-key" );
2047+ meta2 = lyd_find_meta (diff_iter -> meta , mod , "key" );
2048+
2049+ /* store string values of metadata to compare later */
2050+ name = lyd_get_meta_value (meta1 );
2051+ name_iter = lyd_get_meta_value (meta2 );
2052+
2053+ /* reuse buffers by resetting used size */
2054+ buffused1 = buffused2 = 0 ;
2055+
2056+ if ((ret = lyd_path_list_predicate (* diff , & buff1 , & bufflen1 , & buffused1 , 0 ))) {
2057+ goto cleanup ;
2058+ }
2059+
2060+ if ((ret = lyd_path_list_predicate (diff_iter , & buff2 , & bufflen2 , & buffused2 , 0 ))) {
2061+ goto cleanup ;
2062+ }
2063+
2064+ if (!name || !name_iter ) {
2065+ continue ;
2066+ }
2067+
2068+ /* compare path predicates with metadata - check if this is a reversed operation */
2069+ if (!strcmp (buff1 , name_iter ) && !strcmp (buff2 , name )) {
2070+
2071+ /* found a cyclic change - remove and free the node */
2072+ if ((ret = lyd_unlink_tree (* diff ))) {
2073+ goto cleanup ;
2074+ }
2075+
2076+ lyd_free_tree (* diff );
2077+ * diff = NULL ;
2078+ goto cleanup ;
2079+ }
2080+ }
2081+ cleanup :
2082+ free (buff1 );
2083+ free (buff2 );
2084+ return ret ;
2085+ }
2086+
20072087/**
20082088 * @brief Update operations on a diff node when the new operation is REPLACE.
20092089 *
@@ -2047,6 +2127,7 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
20472127 meta = lyd_find_meta (src_diff -> meta , mod , meta_name );
20482128 LY_CHECK_ERR_RET (!meta , LOGERR_META (ctx , meta_name , src_diff ), LY_EINVAL );
20492129 LY_CHECK_RET (lyd_dup_meta_single (meta , diff_match , NULL ));
2130+
20502131 break ;
20512132 case LYS_LEAF :
20522133 /* replaced with the exact same value, impossible */
@@ -2461,6 +2542,17 @@ lyd_diff_is_redundant(struct lyd_node *diff)
24612542 orig_meta_name = "orig-value" ;
24622543 }
24632544
2545+ /** userordered lists can have different nodes that lead to identical changes.
2546+ * Only one node will stay other is unlinked
2547+ */
2548+ if (!strcmp (meta_name , "key" )) {
2549+ LY_CHECK_RET (lyd_diff_find_and_unlink_cyclic_nodes (& diff , mod ), 0 );
2550+ if (!diff ) {
2551+ return 0 ;
2552+ }
2553+
2554+ }
2555+
24642556 /* check for redundant move */
24652557 orig_val_meta = lyd_find_meta (diff -> meta , mod , orig_meta_name );
24662558 val_meta = lyd_find_meta (diff -> meta , mod , meta_name );
0 commit comments