@@ -2004,18 +2004,92 @@ 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.
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 * metas [2 ];
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 != NULL ) {
2029+ diff_iter = diff_iter -> prev ;
2030+ metas [0 ] = lyd_find_meta ((* diff )-> meta , mod , "key" );
2031+ metas [1 ] = lyd_find_meta (diff_iter -> meta , mod , "orig-key" );
2032+
2033+ /* If keys don't match, skip - not a candidate for cyclic change */
2034+ if (strcmp (lyd_get_meta_value (metas [0 ]), lyd_get_meta_value (metas [1 ]))) {
2035+ continue ;
2036+ }
2037+
2038+ metas [0 ] = lyd_find_meta ((* diff )-> meta , mod , "orig-key" );
2039+ metas [1 ] = lyd_find_meta (diff_iter -> meta , mod , "key" );
2040+
2041+ /* Store string values of metadata to compare later */
2042+ name = lyd_get_meta_value (metas [0 ]);
2043+ name_iter = lyd_get_meta_value (metas [1 ]);
2044+
2045+ if ((ret = lyd_path_list_predicate (* diff , & buff1 , & bufflen1 , & buffused1 , 0 ))) {
2046+ return ret ;
2047+ }
2048+
2049+ if ((ret = lyd_path_list_predicate (diff_iter , & buff2 , & bufflen2 , & buffused2 , 0 ))) {
2050+ return ret ;
2051+ }
2052+
2053+ /* Compare path predicates with metadata - check if this is a reversed operation */
2054+ if (!strcmp (buff1 , name_iter ) && !strcmp (buff2 , name )) {
2055+
2056+ /* Found a cyclic change - remove and free the node */
2057+ if ((ret = lyd_unlink_tree (* diff ))) {
2058+ return ret ;
2059+ }
2060+
2061+ lyd_free_all (* diff );
2062+ free (buff1 );
2063+ free (buff2 );
2064+ * diff = NULL ;
2065+ return ret ;
2066+ }
2067+
2068+ free (buff1 );
2069+ buff1 = NULL ;
2070+ bufflen1 = 0 ;
2071+ buffused1 = 0 ;
2072+ free (buff2 );
2073+ buff2 = NULL ;
2074+ bufflen2 = 0 ;
2075+ buffused2 = 0 ;
2076+ }
2077+ return ret ;
2078+ }
2079+
20072080/**
20082081 * @brief Update operations on a diff node when the new operation is REPLACE.
20092082 *
2010- * @param[in] diff_match Node from the diff.
2083+ * @param[in,out] diff_node Node from the diff.
20112084 * @param[in] cur_op Current operation of @p diff_match.
20122085 * @param[in] src_diff Current source diff node.
20132086 * @return LY_ERR value.
20142087 */
20152088static LY_ERR
2016- lyd_diff_merge_replace (struct lyd_node * diff_match , enum lyd_diff_op cur_op , const struct lyd_node * src_diff )
2089+ lyd_diff_merge_replace (struct lyd_node * * diff_node , enum lyd_diff_op cur_op , const struct lyd_node * src_diff )
20172090{
20182091 LY_ERR ret ;
2092+ struct lyd_node * diff_match = * diff_node ;
20192093 const char * str_val , * meta_name , * orig_meta_name ;
20202094 struct lyd_meta * meta ;
20212095 const struct lys_module * mod ;
@@ -2047,6 +2121,14 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
20472121 meta = lyd_find_meta (src_diff -> meta , mod , meta_name );
20482122 LY_CHECK_ERR_RET (!meta , LOGERR_META (ctx , meta_name , src_diff ), LY_EINVAL );
20492123 LY_CHECK_RET (lyd_dup_meta_single (meta , diff_match , NULL ));
2124+
2125+ /** userordered lists can have different nodes that lead to identical changes.
2126+ * Only one node will stay other is unlinked
2127+ */
2128+ if (!strcmp (meta_name , "key" )) {
2129+ LY_CHECK_RET (lyd_diff_find_and_unlink_cyclic_nodes (diff_node , mod ));
2130+ }
2131+
20502132 break ;
20512133 case LYS_LEAF :
20522134 /* replaced with the exact same value, impossible */
@@ -2720,7 +2802,13 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent,
27202802 /* merge operations */
27212803 switch (src_op ) {
27222804 case LYD_DIFF_OP_REPLACE :
2723- ret = lyd_diff_merge_replace (diff_node , cur_op , src_diff );
2805+ ret = lyd_diff_merge_replace (& diff_node , cur_op , src_diff );
2806+
2807+ /* diff_node was unlinked and do not need any further changes */
2808+ if (!diff_node ) {
2809+ return LY_SUCCESS ;
2810+ }
2811+
27242812 break ;
27252813 case LYD_DIFF_OP_CREATE :
27262814 if ((cur_op == LYD_DIFF_OP_CREATE ) && lysc_is_dup_inst_list (diff_node -> schema )) {
0 commit comments