@@ -2004,6 +2004,197 @@ 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+  *        Check whether this diff node is redundant. 
2011+  * 
2012+  * @param[in,out] diff The node whose metadata has been modified. 
2013+  * @param[in] child The child of diff node. 
2014+  * @return 0 if not, non-zero if it is. 
2015+  */ 
2016+ static  ly_bool 
2017+ lyd_diff_is_redundant_userord_move (struct  lyd_node  * * diff , struct  lyd_node  * child )
2018+ {
2019+     LY_ERR  ret  =  LY_SUCCESS ;
2020+     struct  lyd_meta  * meta1 , * meta2 ;
2021+     struct  lyd_meta  * orig_val_meta  =  NULL , * val_meta  =  NULL ;
2022+     struct  lyd_node  * diff_iter  =  * diff ;
2023+     char  * buff1  =  NULL , * buff2  =  NULL ;
2024+     const  char  * llist_value1  =  NULL , * llist_value2  =  NULL ;
2025+     const  char  * name  =  NULL , * name_iter  =  NULL ;
2026+     size_t  bufflen1  =  0 , buffused1  =  0 ;
2027+     size_t  bufflen2  =  0 , buffused2  =  0 ;
2028+     const  char  * orig_meta_name , * meta_name ;
2029+ 
2030+     /* get metadata names */ 
2031+     if  (lysc_is_dup_inst_list ((* diff )-> schema )) {
2032+         meta_name  =  "yang:position" ;
2033+         orig_meta_name  =  "yang:orig-position" ;
2034+     } else  if  ((* diff )-> schema -> nodetype  ==  LYS_LIST ) {
2035+         meta_name  =  "yang:key" ;
2036+         orig_meta_name  =  "yang:orig-key" ;
2037+     } else  {
2038+         meta_name  =  "yang:value" ;
2039+         orig_meta_name  =  "yang:orig-value" ;
2040+     }
2041+ 
2042+     /* check for redundant move */ 
2043+     orig_val_meta  =  lyd_find_meta ((* diff )-> meta , NULL , orig_meta_name );
2044+     val_meta  =  lyd_find_meta ((* diff )-> meta , NULL , meta_name );
2045+     assert (orig_val_meta  &&  val_meta );
2046+ 
2047+     if  (!lyd_compare_meta (orig_val_meta , val_meta )) {
2048+         /* there is actually no move */ 
2049+         lyd_free_meta_single (orig_val_meta );
2050+         lyd_free_meta_single (val_meta );
2051+         if  (child ) {
2052+             /* change operation to NONE, we have siblings */ 
2053+             lyd_diff_change_op ((* diff ), LYD_DIFF_OP_NONE );
2054+             goto cleanup ;
2055+         }
2056+ 
2057+         /* redundant node, BUT !! 
2058+             * In diff the move operation is always converted to be INSERT_AFTER, which is fine 
2059+             * because the data that this is applied on should not change for the diff lifetime. 
2060+             * However, when we are merging 2 diffs, this conversion is actually lossy because 
2061+             * if the data change, the move operation can also change its meaning. In this specific 
2062+             * case the move operation will be lost. But it can be considered a feature, it is not supported. 
2063+             */ 
2064+         ret  =  1 ;
2065+         goto cleanup ;
2066+     }
2067+ 
2068+     /* itereate throught previous nodes and look for logically identical changes */ 
2069+     diff_iter  =  (* diff )-> prev ;
2070+     while  (diff_iter  !=  (* diff )) {
2071+ 
2072+         meta1  =  lyd_find_meta ((* diff )-> meta , NULL , meta_name );
2073+         meta2  =  lyd_find_meta (diff_iter -> meta , NULL , orig_meta_name );
2074+ 
2075+         name  =  lyd_get_meta_value (meta1 );
2076+         name_iter  =  lyd_get_meta_value (meta2 );
2077+ 
2078+         if  (!name  ||  !name_iter ) {
2079+             goto next_iter ;
2080+         }
2081+ 
2082+         /* if keys don't match, skip - not a candidate for cyclic change */ 
2083+         if  (strcmp (name , name_iter )) {
2084+             goto next_iter ;
2085+         }
2086+ 
2087+         meta1  =  lyd_find_meta ((* diff )-> meta , NULL , orig_meta_name );
2088+         meta2  =  lyd_find_meta (diff_iter -> meta , NULL , meta_name );
2089+ 
2090+         /* store string values of metadata to compare later */ 
2091+         name  =  lyd_get_meta_value (meta1 );
2092+         name_iter  =  lyd_get_meta_value (meta2 );
2093+ 
2094+         if  (!name  ||  !name_iter ) {
2095+             goto next_iter ;
2096+         }
2097+ 
2098+         if  ((* diff )-> schema -> nodetype  ==  LYS_LIST ) {
2099+ 
2100+             /* reuse buffers by resetting used size */ 
2101+             buffused1  =  buffused2  =  0 ;
2102+             LY_CHECK_GOTO (ret  =  lyd_path_list_predicate (* diff , & buff1 , & bufflen1 , & buffused1 , 0 ), cleanup );
2103+ 
2104+             LY_CHECK_GOTO (ret  =  lyd_path_list_predicate (diff_iter , & buff2 , & bufflen2 , & buffused2 , 0 ), cleanup );
2105+ 
2106+             /* compare path predicates with metadata - check if this is a reversed operation */ 
2107+             if  (!strcmp (buff1 , name_iter ) &&  !strcmp (buff2 , name )) {
2108+ 
2109+                 /* found a cyclic change - remove and free the node */ 
2110+                 ret  =  1 ;
2111+                 goto cleanup ;
2112+             }
2113+         } else  {
2114+             llist_value1  =  lyd_get_value (* diff );
2115+ 
2116+             llist_value2  =  lyd_get_value (diff_iter );
2117+ 
2118+             /* compare vlaue of data node with metadata - check if this is a reversed operation */ 
2119+             if  (!strcmp (llist_value1 , name_iter ) &&  !strcmp (llist_value2 , name )) {
2120+ 
2121+                 /* found a cyclic change - remove and free the node */ 
2122+                 ret  =  1 ;
2123+                 goto cleanup ;
2124+             }
2125+         }
2126+     next_iter :
2127+         diff_iter  =  diff_iter -> prev ;
2128+     }
2129+ 
2130+ cleanup :
2131+     free (buff1 );
2132+     free (buff2 );
2133+     return  ret ;
2134+ }
2135+ 
2136+ /** 
2137+  * @brief Propagate key/value metadata from a list or leaf-list node 
2138+  *        to its sibling nodes that reference it via key/value. 
2139+  * 
2140+  *        This is used to ensure correct ordering in user-ordered lists 
2141+  *        or leaf-lists by updating the corresponding metadata in sibling 
2142+  *        nodes before the reference node changes. 
2143+  * 
2144+  * @param[in] diff Node from lyd_diff_merge_replace(). 
2145+  * @param[in] meta_name Name of the metadata ("key" or "value"). 
2146+  * @return LY_ERR value. 
2147+  */ 
2148+ static  LY_ERR 
2149+ lyd_diff_propagate_meta (struct  lyd_node  * diff , const  char  * meta_name )
2150+ {
2151+     const  struct  lys_module  * mod ;
2152+     struct  lyd_meta  * meta1 , * meta2 ;
2153+     struct  lyd_node  * diff_iter  =  NULL ;
2154+     char  * buff  =  NULL ;
2155+     size_t  bufflen  =  0 , buffused  =  0 ;
2156+     const  char  * meta_value ;
2157+ 
2158+     /* get "yang" module for the metadata */ 
2159+     mod  =  ly_ctx_get_module_latest (LYD_CTX (diff ), "yang" );
2160+     assert (mod );
2161+ 
2162+     /* 
2163+      * iterate through all siblings of the diff 
2164+      * if a sibling references the diff node via metadata, update it 
2165+      */ 
2166+     LY_LIST_FOR (diff , diff_iter ) {
2167+         /* find the relevant metadata on the current sibling */ 
2168+         if  ((meta1  =  lyd_find_meta (diff_iter -> meta , mod , meta_name ))) {
2169+             if  (diff -> schema -> nodetype  ==  LYS_LEAFLIST ) {
2170+                 if  (!strcmp (lyd_get_meta_value (meta1 ), lyd_get_value (diff ))) {
2171+                     /* replace the old metadata with the updated one from the changed node */ 
2172+                     lyd_diff_del_meta (diff_iter , meta_name );
2173+                     meta2  =  lyd_find_meta (diff -> meta , mod , meta_name );
2174+                     LY_CHECK_GOTO (lyd_dup_meta_single (meta2 , diff_iter , NULL ), cleanup );
2175+                 }
2176+             } else  {
2177+ 
2178+                 buffused  =  0 ;
2179+ 
2180+                 LY_CHECK_GOTO (lyd_path_list_predicate (diff , & buff , & bufflen , & buffused , 0 ), cleanup );
2181+                 meta_value  =  lyd_get_meta_value (meta1 );
2182+ 
2183+                 /* if the path predicate matches, replace the metadata */ 
2184+                 if  (!strcmp (buff , meta_value )) {
2185+                     lyd_diff_del_meta (diff_iter , meta_name );
2186+                     meta2  =  lyd_find_meta (diff -> meta , mod , meta_name );
2187+                     LY_CHECK_GOTO (lyd_dup_meta_single (meta2 , diff_iter , NULL ), cleanup );
2188+                 }
2189+             }
2190+         }
2191+     }
2192+ 
2193+ cleanup :
2194+     free (buff );
2195+     return  LY_SUCCESS ;
2196+ }
2197+ 
20072198/** 
20082199 * @brief Update operations on a diff node when the new operation is REPLACE. 
20092200 * 
@@ -2043,10 +2234,14 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
20432234                meta_name  =  "value" ;
20442235            }
20452236
2237+             /* update sibling nodes which reference the diff_match by key/value */ 
2238+             LY_CHECK_RET (lyd_diff_propagate_meta (diff_match , meta_name ));
2239+ 
20462240            lyd_diff_del_meta (diff_match , meta_name );
20472241            meta  =  lyd_find_meta (src_diff -> meta , mod , meta_name );
20482242            LY_CHECK_ERR_RET (!meta , LOGERR_META (ctx , meta_name , src_diff ), LY_EINVAL );
20492243            LY_CHECK_RET (lyd_dup_meta_single (meta , diff_match , NULL ));
2244+ 
20502245            break ;
20512246        case  LYS_LEAF :
20522247            /* replaced with the exact same value, impossible */ 
@@ -2429,10 +2624,10 @@ static ly_bool
24292624lyd_diff_is_redundant (struct  lyd_node  * diff )
24302625{
24312626    enum  lyd_diff_op  op ;
2432-     struct  lyd_meta  * meta ,  * orig_val_meta   =   NULL ,  * val_meta   =   NULL ;
2627+     struct  lyd_meta  * meta ;
24332628    struct  lyd_node  * child ;
24342629    const  struct  lys_module  * mod ;
2435-     const  char  * str ,  * orig_meta_name ,  * meta_name ;
2630+     const  char  * str ;
24362631
24372632    assert (diff );
24382633
@@ -2449,42 +2644,12 @@ lyd_diff_is_redundant(struct lyd_node *diff)
24492644    LY_CHECK_RET (lyd_diff_get_op (diff , & op , NULL ), 0 );
24502645
24512646    if  ((op  ==  LYD_DIFF_OP_REPLACE ) &&  lysc_is_userordered (diff -> schema )) {
2452-         /* get metadata names */ 
2453-         if  (lysc_is_dup_inst_list (diff -> schema )) {
2454-             meta_name  =  "position" ;
2455-             orig_meta_name  =  "orig-position" ;
2456-         } else  if  (diff -> schema -> nodetype  ==  LYS_LIST ) {
2457-             meta_name  =  "key" ;
2458-             orig_meta_name  =  "orig-key" ;
2459-         } else  {
2460-             meta_name  =  "value" ;
2461-             orig_meta_name  =  "orig-value" ;
2462-         }
2463- 
2464-         /* check for redundant move */ 
2465-         orig_val_meta  =  lyd_find_meta (diff -> meta , mod , orig_meta_name );
2466-         val_meta  =  lyd_find_meta (diff -> meta , mod , meta_name );
2467-         assert (orig_val_meta  &&  val_meta );
2468- 
2469-         if  (!lyd_compare_meta (orig_val_meta , val_meta )) {
2470-             /* there is actually no move */ 
2471-             lyd_free_meta_single (orig_val_meta );
2472-             lyd_free_meta_single (val_meta );
2473-             if  (child ) {
2474-                 /* change operation to NONE, we have siblings */ 
2475-                 lyd_diff_change_op (diff , LYD_DIFF_OP_NONE );
2476-                 return  0 ;
2477-             }
24782647
2479-             /* redundant node, BUT !! 
2480-              * In diff the move operation is always converted to be INSERT_AFTER, which is fine 
2481-              * because the data that this is applied on should not change for the diff lifetime. 
2482-              * However, when we are merging 2 diffs, this conversion is actually lossy because 
2483-              * if the data change, the move operation can also change its meaning. In this specific 
2484-              * case the move operation will be lost. But it can be considered a feature, it is not supported. 
2485-              */ 
2486-             return  1 ;
2487-         }
2648+         /** userordered lists can have different nodes that lead to identical changes. 
2649+          *  if such a redundant node is detected, this function returns non-zero. 
2650+          */ 
2651+         LY_CHECK_RET (lyd_diff_is_redundant_userord_move (& diff , child ), 1 );
2652+ 
24882653    } else  if  (op  ==  LYD_DIFF_OP_NONE ) {
24892654        if  (!diff -> schema ) {
24902655            /* opaque node with none must be redundant */ 
@@ -3108,6 +3273,7 @@ lyd_diff_reverse_userord(struct lyd_node *node, const struct lysc_node **schema_
31083273            do  {
31093274                -- u ;
31103275                LY_CHECK_GOTO (rc  =  lyd_insert_after (anchor , (* nodes_p )[u ]), cleanup );
3276+                 anchor  =  (* nodes_p )[u ];
31113277            } while  (u );
31123278        }
31133279
@@ -3268,13 +3434,13 @@ lyd_diff_reverse_siblings_r(struct lyd_node *sibling, const struct lys_module *y
32683434        LY_CHECK_GOTO (rc  =  lyd_diff_reverse_siblings_r (lyd_child (iter ), yang_mod ), cleanup );
32693435
32703436        if  (lysc_is_userordered (iter -> schema )) {
3271-             /* special user-ordered nodes processing */ 
3437+             /* special user-ordered nodes processing (collect all the nodes)  */ 
32723438            LY_CHECK_GOTO (rc  =  lyd_diff_reverse_userord (iter , & userord_schema , & userord ), cleanup );
32733439        }
32743440    }
32753441
32763442    if  (userord_schema ) {
3277-         /* finish user-ordered nodes processing */ 
3443+         /* finish user-ordered nodes processing - reverse the order of the nodes  */ 
32783444        LY_CHECK_GOTO (rc  =  lyd_diff_reverse_userord (NULL , & userord_schema , & userord ), cleanup );
32793445    }
32803446
0 commit comments