validation NEW duplicate instances
Also trees structure removed, some
refactoring included.
diff --git a/src/validation.c b/src/validation.c
index 97f02d6..96246cd 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -20,16 +20,67 @@
#include "tree_data_internal.h"
#include "tree_schema_internal.h"
+static struct lyd_node *
+lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling, const struct lysc_node **slast,
+ const struct lysc_node *parent, const struct lysc_module *module)
+{
+ const struct lysc_node *siter = NULL;
+ struct lyd_node *match = NULL;
+
+ assert(parent || module);
+ assert(!last || (slast && *slast));
+
+ if (slast) {
+ siter = *slast;
+ }
+
+ if (last && last->next) {
+ /* find next data instance */
+ lyd_find_sibling_next2(last->next, siter, NULL, 0, &match);
+ if (match) {
+ return match;
+ }
+ }
+
+ /* find next schema node data instance */
+ while ((siter = lys_getnext(siter, parent, module, 0))) {
+ switch (siter->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ case LYS_LEAF:
+ lyd_find_sibling_val(sibling, siter, NULL, 0, &match);
+ break;
+ case LYS_LIST:
+ case LYS_LEAFLIST:
+ lyd_find_sibling_next2(sibling, siter, NULL, 0, &match);
+ break;
+ default:
+ assert(0);
+ LOGINT(NULL);
+ }
+
+ if (match) {
+ break;
+ }
+ }
+
+ if (slast) {
+ *slast = siter;
+ }
+ return match;
+}
+
/**
* @brief Evaluate a single "when" condition.
*
* @param[in] when When to evaluate.
* @param[in] node Node whose existence depends on this when.
- * @param[in] trees Array of all data trees.
+ * @param[in] tree Data tree.
* @return LY_ERR value (LY_EINCOMPLETE if a referenced node does not have its when evaluated)
*/
static LY_ERR
-lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node **trees)
+lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node *tree)
{
LY_ERR ret;
const struct lyd_node *ctx_node;
@@ -46,7 +97,7 @@
/* evaluate when */
ret = lyxp_eval(when->cond, LYD_UNKNOWN, when->module, ctx_node, ctx_node ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG,
- trees, &xp_set, LYXP_SCHEMA);
+ tree, &xp_set, LYXP_SCHEMA);
lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
/* return error or LY_EINCOMPLETE for dependant unresolved when */
@@ -72,7 +123,7 @@
LY_ERR
lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
- ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees)
+ ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node *tree)
{
LY_ERR ret = LY_SUCCESS;
uint32_t u;
@@ -83,7 +134,7 @@
/* validate and store the value of the node */
ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1, get_prefix_clb,
- parser_data, format, trees);
+ parser_data, format, tree);
LY_CHECK_RET(ret);
}
@@ -93,7 +144,7 @@
/* validate and store the value of the node */
ret = lyd_value_parse_attr(attr->parent->schema->module->ctx, attr, attr->value.original,
- strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, trees);
+ strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, tree);
LY_CHECK_RET(ret);
}
@@ -116,7 +167,7 @@
do {
uint32_t i;
LY_ARRAY_FOR(schema->when, i) {
- ret = lyd_val_when(schema->when[i], node, trees);
+ ret = lyd_val_when(schema->when[i], node, tree);
if (ret) {
break;
}
@@ -160,47 +211,12 @@
return ly_ctx_get_module_iter(ctx, i);
}
-static int
-lyd_val_has_choice_data(const struct lysc_node *snode, struct lyd_node *sibling)
-{
- const struct lysc_node *iter = NULL;
-
- assert(snode->nodetype == LYS_CHOICE);
-
- while ((iter = lys_getnext(iter, snode, NULL, 0))) {
- switch (iter->nodetype) {
- case LYS_CONTAINER:
- case LYS_ANYXML:
- case LYS_ANYDATA:
- case LYS_LEAF:
- if (!lyd_find_sibling_val(sibling, iter, NULL, 0, NULL)) {
- /* one case child data instance found */
- return 1;
- }
- break;
- case LYS_LIST:
- case LYS_LEAFLIST:
- if (!lyd_find_sibling_next2(sibling, iter, NULL, 0, NULL)) {
- /* one case child data instance found */
- return 1;
- }
- break;
- default:
- assert(0);
- LOGINT(snode->module->ctx);
- return 0;
- }
- }
-
- return 0;
-}
-
static LY_ERR
lyd_validate_mandatory(const struct lysc_node *snode, struct lyd_node *sibling)
{
if (snode->nodetype == LYS_CHOICE) {
/* some data of a choice case exist */
- if (lyd_val_has_choice_data(snode, sibling)) {
+ if (lys_getnext_data(NULL, sibling, NULL, snode, NULL)) {
return LY_SUCCESS;
}
} else {
@@ -501,15 +517,70 @@
}
static LY_ERR
-lyd_validate_cases(const struct lysc_node_case *cases, struct lyd_node *sibling)
+lyd_validate_cases(const struct lysc_node_choice *choic, struct lyd_node **sibling)
{
- /* TODO check there are nodes only from a single case,
- * what if not? validation error or autodelete */
+ const struct lysc_node *scase, *iter, *old_case = NULL, *new_case = NULL;
+ struct lyd_node *match, *to_del;
+ int found;
+
+ LY_LIST_FOR((struct lysc_node *)choic->cases, scase) {
+ found = 0;
+ iter = NULL;
+ match = NULL;
+ while ((match = lys_getnext_data(match, *sibling, &iter, scase, NULL))) {
+ if (match->flags & LYD_NEW) {
+ /* a new case data found, nothing more to look for */
+ found = 2;
+ break;
+ } else {
+ /* and old case data found */
+ if (found == 0) {
+ found = 1;
+ }
+ }
+ }
+
+ if (found == 1) {
+ /* there cannot be 2 old cases */
+ assert(!old_case);
+
+ /* remember an old existing case */
+ old_case = scase;
+ } else if (found == 2) {
+ if (new_case) {
+ /* new data from 2 cases */
+ LOGVAL(choic->module->ctx, LY_VLOG_LYSC, choic, LY_VCODE_DUPCASE, new_case->name, scase->name);
+ return LY_EVALID;
+ }
+
+ /* remember a new existing case */
+ new_case = scase;
+ }
+ }
+
+ if (old_case && new_case) {
+ /* auto-delete old case */
+ iter = NULL;
+ match = NULL;
+ to_del = NULL;
+ while ((match = lys_getnext_data(match, *sibling, &iter, old_case, NULL))) {
+ if ((*sibling == to_del) && !(*sibling)->parent) {
+ *sibling = (*sibling)->next;
+ }
+ lyd_free_tree(to_del);
+ to_del = match;
+ }
+ if ((*sibling == to_del) && !(*sibling)->parent) {
+ *sibling = (*sibling)->next;
+ }
+ lyd_free_tree(to_del);
+ }
+
return LY_SUCCESS;
}
static LY_ERR
-lyd_validate_siblings_schema_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod,
+lyd_validate_siblings_schema_r(struct lyd_node **sibling, const struct lysc_node *sparent, const struct lysc_module *mod,
int options)
{
const struct lysc_node *snode = NULL;
@@ -521,25 +592,25 @@
if (snode->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
slist = (struct lysc_node_list *)snode;
if (slist->min || slist->max) {
- LY_CHECK_RET(lyd_validate_minmax(snode, slist->min, slist->max, sibling));
+ LY_CHECK_RET(lyd_validate_minmax(snode, slist->min, slist->max, *sibling));
}
/* check generic mandatory existence */
} else if (snode->flags & LYS_MAND_TRUE) {
- LY_CHECK_RET(lyd_validate_mandatory(snode, sibling));
+ LY_CHECK_RET(lyd_validate_mandatory(snode, *sibling));
}
/* check unique */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->uniques) {
- LY_CHECK_RET(lyd_validate_unique(snode, slist->uniques, sibling));
+ LY_CHECK_RET(lyd_validate_unique(snode, slist->uniques, *sibling));
}
}
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
- LY_CHECK_RET(lyd_validate_cases(((struct lysc_node_choice *)snode)->cases, sibling));
+ LY_CHECK_RET(lyd_validate_cases((struct lysc_node_choice *)snode, sibling));
}
if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
@@ -552,50 +623,115 @@
}
static LY_ERR
-lyd_validate_siblings_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+lyd_validate_dup_nodes(struct lyd_node *sibling, struct lyd_node *node)
{
- struct lyd_node *node;
+ struct lyd_node **match_p;
+ int fail = 0;
+
+ if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (node->schema->flags & LYS_CONFIG_R)) {
+ /* duplicate instances allowed */
+ return LY_SUCCESS;
+ }
+
+ /* find exactly the same next instance using hashes if possible */
+ if (node->parent && node->parent->children_ht) {
+ if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
+ fail = 1;
+ }
+ } else {
+ for (; sibling; sibling = sibling->next) {
+ if (sibling == node) {
+ continue;
+ }
+
+ if (node->schema->nodetype & (LYD_NODE_ANY | LYS_LEAF)) {
+ if (sibling->schema == node->schema) {
+ fail = 1;
+ break;
+ }
+ } else if (!lyd_compare(sibling, node, 0)) {
+ fail = 1;
+ break;
+ }
+ }
+ }
+
+ if (fail) {
+ LOGVAL(node->schema->module->ctx, LY_VLOG_LYD, node, LY_VCODE_DUP, node->schema->name);
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_validate_siblings_r(struct lyd_node **sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+{
+ struct lyd_node *next, *node, *match;
+
+ assert(sibling);
/* validate all restrictions of nodes themselves */
- LY_LIST_FOR(sibling, node) {
- /* TODO node instance duplicities */
+ LY_LIST_FOR_SAFE(*sibling, next, node) {
+ if (node->flags & LYD_NEW) {
+ /* new node instance duplicities */
+ LY_CHECK_RET(lyd_validate_dup_nodes(*sibling, node));
+ } else if (node->flags & LYD_DEFAULT) {
+ /* remove default if there is an explicit instance */
+ assert(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER));
+ if (node->schema->nodetype == LYS_LEAFLIST) {
+ lyd_find_sibling_next2(*sibling, node->schema, NULL, 0, &match);
+ } else {
+ lyd_find_sibling_val(*sibling, node->schema, NULL, 0, &match);
+ }
+ while (match && (match->flags & LYD_DEFAULT) && !(match->flags & LYD_NEW)) {
+ lyd_find_sibling_next2(match->next, node->schema, NULL, 0, &match);
+ }
+
+ if (match) {
+ /* non-default (or at least new for container) instance found, remove the default one */
+ if ((*sibling == node) && !(*sibling)->parent) {
+ *sibling = (*sibling)->next;
+ }
+ lyd_free_tree(node);
+ continue;
+ }
+ }
+
/* TODO node's must */
/* TODO node status */
/* TODO node's if-features */
- /* TODO node list keys */
+ /* TODO list all keys existence */
/* node value including if-feature is checked by plugins */
}
/* validate schema-based restrictions */
LY_CHECK_RET(lyd_validate_siblings_schema_r(sibling, sparent, mod, options));
- LY_LIST_FOR(sibling, node) {
+ LY_LIST_FOR(*sibling, node) {
/* this sibling is valid */
node->flags &= ~LYD_NEW;
/* validate all children recursively */
- LY_CHECK_RET(lyd_validate_siblings_r((struct lyd_node *)lyd_node_children(node), node->schema, mod, options));
+ if (node->schema->nodetype & LYD_NODE_INNER) {
+ LY_CHECK_RET(lyd_validate_siblings_r(lyd_node_children_p(node), node->schema, mod, options));
+ }
}
return LY_SUCCESS;
}
LY_ERR
-lyd_validate_data(const struct lyd_node **trees, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
+lyd_validate_data(struct lyd_node **tree, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
int val_opts)
{
- uint32_t i = 0, j;
+ uint32_t i = 0;
const struct lys_module *mod;
- struct lyd_node *tree;
if (val_opts & LYD_VALOPT_DATA_ONLY) {
- if (trees) {
- for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
- tree = (struct lyd_node *)trees[j];
-
- /* validate all top-level nodes and then inner nodes recursively */
- LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, tree->schema->module->compiled, val_opts));
- }
+ if (*tree) {
+ /* TODO all modules, not just first */
+ /* validate all top-level nodes and then inner nodes recursively */
+ LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, (*tree)->schema->module->compiled, val_opts));
}
} else {
while ((mod = lyd_val_next_module(modules, mod_count, ctx, &i))) {
@@ -603,17 +739,6 @@
continue;
}
- /* find data of this module, if any */
- tree = NULL;
- if (trees) {
- for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
- if (trees[j]->schema->module == mod) {
- tree = (struct lyd_node *)trees[j];
- break;
- }
- }
- }
-
/* validate all top-level nodes and then inner nodes recursively */
LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, mod->compiled, val_opts));
}
@@ -636,7 +761,7 @@
while ((iter = lys_getnext(iter, schema, mod, LYS_GETNEXT_WITHCHOICE))) {
switch (iter->nodetype) {
case LYS_CHOICE:
- if (((struct lysc_node_choice *)iter)->dflt && !lyd_val_has_choice_data(iter, *first)) {
+ if (((struct lysc_node_choice *)iter)->dflt && !lys_getnext_data(NULL, *first, NULL, iter, NULL)) {
/* create default case data */
LY_CHECK_RET(lyd_validate_defaults_r(parent, first, (struct lysc_node *)((struct lysc_node_choice *)iter)->dflt,
NULL, node_types, node_when));
@@ -726,3 +851,58 @@
return LY_SUCCESS;
}
+
+API LY_ERR
+lyd_validate(const struct ly_ctx *ctx, struct lyd_node **tree, int val_opts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *root, *next, *elem;
+ const struct lyd_attr *attr;
+ struct ly_set type_check = {0}, type_attr_check = {0}, when_check = {0};
+
+ LY_CHECK_ARG_RET(ctx, tree, ctx || *tree, LY_EINVAL);
+
+ if (!ctx) {
+ ctx = (*tree)->schema->module->ctx;
+ }
+
+ /* add nested defaults, collect all types and nodes with when condition */
+ LY_LIST_FOR(*tree, root) {
+ LYD_TREE_DFS_BEGIN(root, next, elem) {
+ LY_LIST_FOR(elem->attr, attr) {
+ ly_set_add(&type_attr_check, (void *)attr, LY_SET_OPT_USEASLIST);
+ }
+ if (elem->schema->nodetype & LYD_NODE_TERM) {
+ ly_set_add(&type_check, (void *)elem, LY_SET_OPT_USEASLIST);
+ } else if (elem->schema->nodetype & LYD_NODE_INNER) {
+ ret = lyd_validate_defaults_r((struct lyd_node_inner *)elem, lyd_node_children_p((struct lyd_node *)elem),
+ elem->schema, NULL, &type_check, &when_check);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ if (!(elem->schema->nodetype & (LYS_ACTION | LYS_NOTIF)) && elem->schema->when) {
+ ly_set_add(&when_check, (void *)elem, LY_SET_OPT_USEASLIST);
+ }
+
+ LYD_TREE_DFS_END(root, next, elem);
+ }
+ }
+
+ /* add top-level default nodes */
+ ret = lyd_validate_defaults_top(tree, NULL, 0, (struct ly_ctx *)ctx, &type_check, &when_check, val_opts);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* finish incompletely validated terminal values/attributes and when conditions */
+ ret = lyd_validate_unres(&type_check, &type_attr_check, &when_check, LYD_JSON, lydjson_resolve_prefix, NULL,
+ (const struct lyd_node *)tree);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* context node and other validation tasks that depend on other data nodes */
+ ret = lyd_validate_data(tree, NULL, 0, (struct ly_ctx *)ctx, val_opts);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ ly_set_erase(&type_check, NULL);
+ ly_set_erase(&type_attr_check, NULL);
+ ly_set_erase(&when_check, NULL);
+ return ret;
+}