tree data UPDATE compare (leaf-)lists in different order
diff --git a/src/tree_data.c b/src/tree_data.c
index b2cf4d3..5d2d490 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -52,6 +52,9 @@
#include "xml.h"
#include "xpath.h"
+static LY_ERR lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked);
+
static LYD_FORMAT
lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format)
{
@@ -1264,30 +1267,21 @@
}
/**
- * @brief Internal implementation of @ref lyd_compare_single.
- * @copydoc lyd_compare_single
- * @param[in] parental_schemas_checked Flag used for optimization.
- * When this function is called for the first time, the flag must be set to 0.
- * The @ref lyd_compare_schema_parents_equal should be called only once during
- * recursive calls, and this is accomplished by setting to 1 in the lyd_compare_single_ body.
+ * @brief Compare 2 data nodes if they are equivalent regarding the schema tree.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
*/
static LY_ERR
-lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+lyd_compare_single_schema(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
ly_bool parental_schemas_checked)
{
- const struct lyd_node *iter1, *iter2;
- struct lyd_node_any *any1, *any2;
- int len1, len2;
- LY_ERR r;
-
- if (!node1 || !node2) {
- if (node1 == node2) {
- return LY_SUCCESS;
- } else {
- return LY_ENOT;
- }
- }
-
if (LYD_CTX(node1) == LYD_CTX(node2)) {
/* same contexts */
if (options & LYD_COMPARE_OPAQ) {
@@ -1312,6 +1306,28 @@
}
}
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compare 2 data nodes if they are equivalent regarding the data they contain.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
+ */
+static LY_ERR
+lyd_compare_single_data(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ const struct lyd_node *iter1, *iter2;
+ struct lyd_node_any *any1, *any2;
+ int len1, len2;
+ LY_ERR r;
+
if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) {
return LY_ENOT;
}
@@ -1330,9 +1346,7 @@
}
if (options & LYD_COMPARE_FULL_RECURSION) {
- iter1 = lyd_child(node1);
- iter2 = lyd_child(node2);
- goto all_children_compare;
+ return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1);
}
return LY_SUCCESS;
} else {
@@ -1359,9 +1373,7 @@
}
}
if (options & LYD_COMPARE_FULL_RECURSION) {
- iter1 = lyd_child(node1);
- iter2 = lyd_child(node2);
- goto all_children_compare;
+ return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1);
}
return LY_SUCCESS;
case LYS_LIST:
@@ -1373,31 +1385,23 @@
for (const struct lysc_node *key = lysc_node_child(node1->schema);
key && (key->flags & LYS_KEY);
key = key->next) {
- if (lyd_compare_single_(iter1, iter2, options, parental_schemas_checked)) {
- return LY_ENOT;
+ if (!node1 || !node2) {
+ return (node1 == node2) ? LY_SUCCESS : LY_ENOT;
}
+ r = lyd_compare_single_schema(iter1, iter2, options, 1);
+ LY_CHECK_RET(r);
+ r = lyd_compare_single_data(iter1, iter2, options);
+ LY_CHECK_RET(r);
+
iter1 = iter1->next;
iter2 = iter2->next;
}
+
+ return LY_SUCCESS;
} else {
/* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */
-
-all_children_compare:
- if (!iter1 && !iter2) {
- /* no children, nothing to compare */
- return LY_SUCCESS;
- }
-
- for ( ; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) {
- if (lyd_compare_single_(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION, parental_schemas_checked)) {
- return LY_ENOT;
- }
- }
- if (iter1 || iter2) {
- return LY_ENOT;
- }
+ return lyd_compare_siblings_(iter1, iter2, options, 1);
}
- return LY_SUCCESS;
case LYS_ANYXML:
case LYS_ANYDATA:
any1 = (struct lyd_node_any *)node1;
@@ -1408,9 +1412,7 @@
}
switch (any1->value_type) {
case LYD_ANYDATA_DATATREE:
- iter1 = any1->value.tree;
- iter2 = any2->value.tree;
- goto all_children_compare;
+ return lyd_compare_siblings_(any1->value.tree, any2->value.tree, options, 1);
case LYD_ANYDATA_STRING:
case LYD_ANYDATA_XML:
case LYD_ANYDATA_JSON:
@@ -1440,23 +1442,77 @@
return LY_EINT;
}
+/**
+ * @brief Compare all siblings at a node level.
+ *
+ * @param[in] node1 First sibling list.
+ * @param[in] node2 Second sibling list.
+ * @param[in] options Various @ref datacompareoptions.
+ * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match.
+ * @return LY_SUCCESS if equal.
+ * @return LY_ENOT if not equal.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked)
+{
+ LY_ERR r;
+ const struct lyd_node *iter2;
+
+ while (node1 && node2) {
+ /* schema match */
+ r = lyd_compare_single_schema(node1, node2, options, parental_schemas_checked);
+ LY_CHECK_RET(r);
+
+ if (node1->schema && (((node1->schema->nodetype == LYS_LIST) && !(node1->schema->flags & LYS_KEYLESS)) ||
+ ((node1->schema->nodetype == LYS_LEAFLIST) && (node1->schema->flags & LYS_CONFIG_W))) &&
+ (node1->schema->flags & LYS_ORDBY_SYSTEM)) {
+ /* find a matching instance in case they are ordered differently */
+ r = lyd_find_sibling_first(node2, node1, (struct lyd_node **)&iter2);
+ if (r == LY_ENOTFOUND) {
+ /* no matching instance, data not equal */
+ r = LY_ENOT;
+ }
+ LY_CHECK_RET(r);
+ } else {
+ /* compare with the current node */
+ iter2 = node2;
+ }
+
+ /* data match */
+ r = lyd_compare_single_data(node1, iter2, options | LYD_COMPARE_FULL_RECURSION);
+ LY_CHECK_RET(r);
+
+ node1 = node1->next;
+ node2 = node2->next;
+ }
+
+ return (node1 || node2) ? LY_ENOT : LY_SUCCESS;
+}
+
LIBYANG_API_DEF LY_ERR
lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
{
- return lyd_compare_single_(node1, node2, options, 0);
+ LY_ERR r;
+
+ if (!node1 || !node2) {
+ return (node1 == node2) ? LY_SUCCESS : LY_ENOT;
+ }
+
+ /* schema match */
+ if ((r = lyd_compare_single_schema(node1, node2, options, 0))) {
+ return r;
+ }
+
+ /* data match */
+ return lyd_compare_single_data(node1, node2, options);
}
LIBYANG_API_DEF LY_ERR
lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
{
- for ( ; node1 && node2; node1 = node1->next, node2 = node2->next) {
- LY_CHECK_RET(lyd_compare_single(node1, node2, options));
- }
-
- if (node1 == node2) {
- return LY_SUCCESS;
- }
- return LY_ENOT;
+ return lyd_compare_siblings_(node1, node2, options, 0);
}
LIBYANG_API_DEF LY_ERR
diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c
index 904c5d8..7b17e50 100644
--- a/tests/utests/data/test_tree_data.c
+++ b/tests/utests/data/test_tree_data.c
@@ -145,6 +145,14 @@
assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
lyd_free_all(tree1);
lyd_free_all(tree2);
+
+ data1 = "<c xmlns=\"urn:tests:a\"><x>c</x><x>a</x><x>b</x></c>";
+ data2 = "<c xmlns=\"urn:tests:a\"><x>a</x><x>b</x><x>c</x></c>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
}
static void