schema compile CHANGE support deviation of min/max-elements
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 06054a6..6f2a367 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -4072,6 +4072,9 @@
* @param[in] mandatory_flag Node's mandatory flag to be applied to the @p node.
* @param[in] nodeid Schema nodeid used to identify target of refine/deviation (for logging).
* @param[in] refine_flag Flag to distinguish if the change is caused by refine (flag set) or deviation (for logging).
+ * @param[in] It is also used as a flag for testing for compatibility with default statement. In case of deviations,
+ * there can be some other deviations of the default properties that we are testing here. To avoid false positive failure,
+ * the tests are skipped here, but they are supposed to be performed after all the deviations are applied.
* @return LY_ERR value.
*/
static LY_ERR
@@ -4088,25 +4091,26 @@
/* check if node has default value */
if (node->nodetype & LYS_LEAF) {
if (node->flags & LYS_SET_DFLT) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid %s of mandatory in \"%s\" - leaf already has \"default\" statement.",
- refine_flag ? "refine" : "deviation", nodeid);
- return LY_EVALID;
+ if (refine_flag) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of mandatory in \"%s\" - leaf already has \"default\" statement.", nodeid);
+ return LY_EVALID;
+ }
} else {
/* remove the default value taken from the leaf's type */
FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
((struct lysc_node_leaf*)node)->dflt = NULL;
}
} else if ((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid %s of mandatory in \"%s\" - choice already has \"default\" statement.",
- refine_flag ? "refine" : "deviation", nodeid);
- return LY_EVALID;
+ if (refine_flag) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of mandatory in \"%s\" - choice already has \"default\" statement.", nodeid);
+ return LY_EVALID;
+ }
}
- if (node->parent && (node->parent->flags & LYS_SET_DFLT)) {
+ if (refine_flag && node->parent && (node->parent->flags & LYS_SET_DFLT)) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid %s of mandatory in \"%s\" under the default case.",
- refine_flag ? "refine" : "deviation", nodeid);
+ "Invalid refine of mandatory in \"%s\" under the default case.", nodeid);
return LY_EVALID;
}
@@ -4775,6 +4779,7 @@
size_t prefix_len, name_len;
const char *prefix, *name, *nodeid;
struct lys_module *mod;
+ uint32_t min, max;
/* get all deviations from the module and all its submodules ... */
LY_ARRAY_FOR(mod_p->deviations, u) {
@@ -4838,12 +4843,28 @@
goto cleanup; \
}
+#define DEV_CHECK_NONPRESENCE_UINT(TYPE, COND, MEMBER, PROPERTY) \
+ if (((TYPE)devs[u]->target)->MEMBER COND) { \
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
+ "Invalid deviation (%s) adding \"%s\" property which already exists (with value \"%u\").", \
+ devs[u]->nodeid, PROPERTY, ((TYPE)devs[u]->target)->MEMBER); \
+ goto cleanup; \
+ }
+
#define DEV_CHECK_PRESENCE(TYPE, COND, MEMBER, DEVTYPE, PROPERTY, VALUE) \
if (!((TYPE)devs[u]->target)->MEMBER || COND) { \
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, devs[u]->nodeid, DEVTYPE, PROPERTY, VALUE); \
goto cleanup; \
}
+#define DEV_CHECK_PRESENCE_UINT(TYPE, COND, MEMBER, PROPERTY) \
+ if (!(((TYPE)devs[u]->target)->MEMBER COND)) { \
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
+ "Invalid deviation (%s) replacing with \"%s\" property \"%u\" which is not present.", \
+ devs[u]->nodeid, PROPERTY, d_rpl->MEMBER); \
+ goto cleanup; \
+ }
+
#define DEV_DEL_MEMBER(TYPE, MEMBER_TRG, MEMBER_DEV, DELFUNC, PROPERTY) \
DEV_CHECK_PRESENCE(TYPE, 0, MEMBER_TRG, "deleting", PROPERTY, d_del->MEMBER_DEV); \
if (strcmp(((TYPE)devs[u]->target)->MEMBER_TRG, d_del->MEMBER_DEV)) { \
@@ -4941,11 +4962,6 @@
/* *default-stmt */
if (d_add->dflts) {
- if (devs[u]->target->flags & LYS_MAND_TRUE) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid deviation (%s) adding \"default\" property \"%s\" into a mandatory node.", devs[u]->nodeid, d_add->dflts[0]);
- goto cleanup;
- }
switch (devs[u]->target->nodetype) {
case LYS_LEAF:
DEV_CHECK_CARDINALITY(d_add->dflts, 1, "default");
@@ -4956,6 +4972,8 @@
((struct lysc_node_leaf*)devs[u]->target)->dflt = NULL;
}
DUP_STRING(ctx->ctx, d_add->dflts[0], ((struct lysc_node_leaf*)devs[u]->target)->dflt);
+ /* mark the new default values as leaf's own */
+ devs[u]->target->flags |= LYS_SET_DFLT;
break;
case LYS_LEAFLIST:
if (((struct lysc_node_leaflist*)devs[u]->target)->dflts && !(devs[u]->target->flags & LYS_SET_DFLT)) {
@@ -4974,7 +4992,7 @@
DUP_STRING(ctx->ctx, d_add->dflts[x - y], ((struct lysc_node_leaflist*)devs[u]->target)->dflts[x]);
LY_ARRAY_INCREMENT(((struct lysc_node_leaflist*)devs[u]->target)->dflts);
}
- /* mark the new default values as leafref's own */
+ /* mark the new default values as leaf-list's own */
devs[u]->target->flags |= LYS_SET_DFLT;
break;
case LYS_CHOICE:
@@ -5022,8 +5040,41 @@
}
/* [min-elements-stmt] */
+ if (d_add->flags & LYS_SET_MIN) {
+ if (devs[u]->target->nodetype == LYS_LEAFLIST) {
+ DEV_CHECK_NONPRESENCE_UINT(struct lysc_node_leaflist*, > 0, min, "min-elements");
+ /* change value */
+ ((struct lysc_node_leaflist*)devs[u]->target)->min = d_add->min;
+ } else if (devs[u]->target->nodetype == LYS_LIST) {
+ DEV_CHECK_NONPRESENCE_UINT(struct lysc_node_list*, > 0, min, "min-elements");
+ /* change value */
+ ((struct lysc_node_list*)devs[u]->target)->min = d_add->min;
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+ lys_nodetype2str(devs[u]->target->nodetype), "add", "min-elements");
+ goto cleanup;
+ }
+ if (d_add->min) {
+ devs[u]->target->flags |= LYS_MAND_TRUE;
+ }
+ }
/* [max-elements-stmt] */
+ if (d_add->flags & LYS_SET_MAX) {
+ if (devs[u]->target->nodetype == LYS_LEAFLIST) {
+ DEV_CHECK_NONPRESENCE_UINT(struct lysc_node_leaflist*, < (uint32_t)-1, max, "max-elements");
+ /* change value */
+ ((struct lysc_node_leaflist*)devs[u]->target)->max = d_add->max ? d_add->max : (uint32_t)-1;
+ } else if (devs[u]->target->nodetype == LYS_LIST) {
+ DEV_CHECK_NONPRESENCE_UINT(struct lysc_node_list*, < (uint32_t)-1, max, "max-elements");
+ /* change value */
+ ((struct lysc_node_list*)devs[u]->target)->max = d_add->max ? d_add->max : (uint32_t)-1;
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+ lys_nodetype2str(devs[u]->target->nodetype), "add", "max-elements");
+ goto cleanup;
+ }
+ }
break;
case LYS_DEV_DELETE:
@@ -5110,9 +5161,13 @@
dflt, "deleting", "default", d_del->dflts[0]);
DEV_DEL_MEMBER(struct lysc_node_leaf*, dflt, dflts[0], lydict_remove, "default");
+ devs[u]->target->flags &= ~LYS_SET_DFLT;
break;
case LYS_LEAFLIST:
DEV_DEL_ARRAY(struct lysc_node_leaflist*, dflts, , , , lydict_remove, "default");
+ if (!((struct lysc_node_leaflist*)devs[u]->target)->dflts) {
+ devs[u]->target->flags &= ~LYS_SET_DFLT;
+ }
break;
case LYS_CHOICE:
DEV_CHECK_CARDINALITY(d_del->dflts, 1, "default");
@@ -5222,8 +5277,41 @@
}
/* [min-elements-stmt] */
+ if (d_rpl->flags & LYS_SET_MIN) {
+ if (devs[u]->target->nodetype == LYS_LEAFLIST) {
+ DEV_CHECK_PRESENCE_UINT(struct lysc_node_leaflist*, > 0, min, "min-elements");
+ /* change value */
+ ((struct lysc_node_leaflist*)devs[u]->target)->min = d_rpl->min;
+ } else if (devs[u]->target->nodetype == LYS_LIST) {
+ DEV_CHECK_PRESENCE_UINT(struct lysc_node_list*, > 0, min, "min-elements");
+ /* change value */
+ ((struct lysc_node_list*)devs[u]->target)->min = d_rpl->min;
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+ lys_nodetype2str(devs[u]->target->nodetype), "replace", "min-elements");
+ goto cleanup;
+ }
+ if (d_rpl->min) {
+ devs[u]->target->flags |= LYS_MAND_TRUE;
+ }
+ }
/* [max-elements-stmt] */
+ if (d_rpl->flags & LYS_SET_MAX) {
+ if (devs[u]->target->nodetype == LYS_LEAFLIST) {
+ DEV_CHECK_PRESENCE_UINT(struct lysc_node_leaflist*, < (uint32_t)-1, max, "max-elements");
+ /* change value */
+ ((struct lysc_node_leaflist*)devs[u]->target)->max = d_rpl->max ? d_rpl->max : (uint32_t)-1;
+ } else if (devs[u]->target->nodetype == LYS_LIST) {
+ DEV_CHECK_PRESENCE_UINT(struct lysc_node_list*, < (uint32_t)-1, max, "max-elements");
+ /* change value */
+ ((struct lysc_node_list*)devs[u]->target)->max = d_rpl->max ? d_rpl->max : (uint32_t)-1;
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+ lys_nodetype2str(devs[u]->target->nodetype), "replace", "max-elements");
+ goto cleanup;
+ }
+ }
break;
default:
@@ -5231,6 +5319,46 @@
goto cleanup;
}
}
+
+ /* check min-max compatibility */
+ if (devs[u]->target->nodetype == LYS_LEAFLIST) {
+ min = ((struct lysc_node_leaflist*)devs[u]->target)->min;
+ max = ((struct lysc_node_leaflist*)devs[u]->target)->max;
+ } else if (devs[u]->target->nodetype == LYS_LIST) {
+ min = ((struct lysc_node_list*)devs[u]->target)->min;
+ max = ((struct lysc_node_list*)devs[u]->target)->max;
+ } else {
+ min = max = 0;
+ }
+ if (min > max) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid combination of min-elements and max-elements "
+ "after deviation (%s): min value %u is bigger than max value %u.",
+ devs[u]->nodeid, min, max);
+ goto cleanup;
+ }
+
+ /* check mandatory - default compatibility */
+ if ((devs[u]->target->nodetype & (LYS_LEAF | LYS_LEAFLIST))
+ && (devs[u]->target->flags & LYS_SET_DFLT)
+ && (devs[u]->target->flags & LYS_MAND_TRUE)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid deviation (%s) combining default value and mandatory %s.",
+ devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype));
+ goto cleanup;
+ } else if ((devs[u]->target->nodetype & LYS_CHOICE)
+ && ((struct lysc_node_choice*)devs[u]->target)->dflt
+ && (devs[u]->target->flags & LYS_MAND_TRUE)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid deviation (%s) combining default case and mandatory choice.", devs[u]->nodeid);
+ goto cleanup;
+ }
+ if (devs[u]->target->parent && (devs[u]->target->parent->flags & LYS_SET_DFLT) && (devs[u]->target->flags & LYS_MAND_TRUE)) {
+ /* mandatory node under a default case */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid deviation (%s) combining mandatory %s \"%s\" in a default choice's case \"%s\".",
+ devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), devs[u]->target->name, devs[u]->target->parent->name);
+ goto cleanup;
+ }
}
ret = LY_SUCCESS;
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 4355725..eddda4c 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -2698,6 +2698,46 @@
assert_false(node->flags & LYS_MAND_MASK); /* just unset on container */
assert_true((lysc_node_children(node)->flags & LYS_MAND_MASK) == LYS_MAND_FALSE);
+ assert_non_null(mod = lys_parse_mem(ctx, "module n {yang-version 1.1; namespace urn:n;prefix n;"
+ "leaf a {default test; type string;}"
+ "leaf b {mandatory true; type string;}"
+ "deviation /a {deviate add {mandatory true;} deviate delete {default test;}}"
+ "deviation /b {deviate add {default test;} deviate replace {mandatory false;}}}", LYS_IN_YANG));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_null(((struct lysc_node_leaf*)node)->dflt);
+ assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+ assert_non_null(node = node->next);
+ assert_string_equal("b", node->name);
+ assert_non_null(((struct lysc_node_leaf*)node)->dflt);
+ assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_FALSE);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module o {namespace urn:o;prefix o;"
+ "leaf-list a {type string;}"
+ "list b {config false;}"
+ "leaf-list c {min-elements 1; max-elements 10; type string;}"
+ "list d {min-elements 10; max-elements 100; config false;}"
+ "deviation /a {deviate add {min-elements 1; max-elements 10;}}"
+ "deviation /b {deviate add {min-elements 10; max-elements 100;}}"
+ "deviation /c {deviate replace {min-elements 10; max-elements 100;}}"
+ "deviation /d {deviate replace {min-elements 1; max-elements 10;}}}", LYS_IN_YANG));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_int_equal(1, ((struct lysc_node_leaflist*)node)->min);
+ assert_int_equal(10, ((struct lysc_node_leaflist*)node)->max);
+ assert_non_null(node = node->next);
+ assert_string_equal("b", node->name);
+ assert_int_equal(10, ((struct lysc_node_list*)node)->min);
+ assert_int_equal(100, ((struct lysc_node_list*)node)->max);
+ assert_non_null(node = node->next);
+ assert_string_equal("c", node->name);
+ assert_int_equal(10, ((struct lysc_node_leaflist*)node)->min);
+ assert_int_equal(100, ((struct lysc_node_leaflist*)node)->max);
+ assert_non_null(node = node->next);
+ assert_string_equal("d", node->name);
+ assert_int_equal(1, ((struct lysc_node_list*)node)->min);
+ assert_int_equal(10, ((struct lysc_node_list*)node)->max);
+
assert_null(lys_parse_mem(ctx, "module aa1 {namespace urn:aa1;prefix aa1;import a {prefix a;}"
"deviation /a:top/a:z {deviate not-supported;}}", LYS_IN_YANG));
logbuf_assert("Invalid absolute-schema-nodeid value \"/a:top/a:z\" - target node not found.");
@@ -2771,7 +2811,7 @@
logbuf_assert("Invalid deviation (/e:a) adding \"default\" property \"e:c\" of choice - mandatory node \"c\" under the default case.");
assert_null(lys_parse_mem(ctx, "module gg5 {namespace urn:gg5;prefix gg5; leaf x {type string; mandatory true;}"
"deviation /x {deviate add {default error;}}}", LYS_IN_YANG));
- logbuf_assert("Invalid deviation (/x) adding \"default\" property \"error\" into a mandatory node.");
+ logbuf_assert("Invalid deviation (/x) combining default value and mandatory leaf.");
assert_null(lys_parse_mem(ctx, "module hh1 {yang-version 1.1; namespace urn:hh1;prefix hh1; import e {prefix e;}"
"deviation /e:d {deviate replace {default hi;}}}", LYS_IN_YANG));
@@ -2822,6 +2862,53 @@
"deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG));
logbuf_assert("Invalid deviation (/x) adding \"mandatory\" property which already exists (with value \"mandatory true\").");
+ assert_null(lys_parse_mem(ctx, "module ll1 {namespace urn:ll1;prefix ll1; leaf x {default test; type string;}"
+ "deviation /x {deviate add {mandatory true;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) combining default value and mandatory leaf.");
+ assert_null(lys_parse_mem(ctx, "module ll2 {yang-version 1.1; namespace urn:ll2;prefix ll2; leaf-list x {default test; type string;}"
+ "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) combining default value and mandatory leaf-list.");
+ assert_null(lys_parse_mem(ctx, "module ll2 {namespace urn:ll2;prefix ll2; choice ch {default a; leaf a {type string;} leaf b {type string;}}"
+ "deviation /ch {deviate add {mandatory true;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/ch) combining default case and mandatory choice.");
+
+ assert_null(lys_parse_mem(ctx, "module mm1 {namespace urn:mm1;prefix mm1; leaf-list x {min-elements 10; type string;}"
+ "deviation /x {deviate add {max-elements 5;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid combination of min-elements and max-elements after deviation (/x): min value 10 is bigger than max value 5.");
+ assert_null(lys_parse_mem(ctx, "module mm2 {namespace urn:mm2;prefix mm2; leaf-list x {max-elements 10; type string;}"
+ "deviation /x {deviate add {min-elements 20;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid combination of min-elements and max-elements after deviation (/x): min value 20 is bigger than max value 10.");
+ assert_null(lys_parse_mem(ctx, "module mm3 {namespace urn:mm3;prefix mm3; list x {min-elements 5; max-elements 10; config false;}"
+ "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid combination of min-elements and max-elements after deviation (/x): min value 5 is bigger than max value 1.");
+ assert_null(lys_parse_mem(ctx, "module mm4 {namespace urn:mm4;prefix mm4; list x {min-elements 5; max-elements 10; config false;}"
+ "deviation /x {deviate replace {min-elements 20;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid combination of min-elements and max-elements after deviation (/x): min value 20 is bigger than max value 10.");
+ assert_null(lys_parse_mem(ctx, "module mm5 {namespace urn:mm5;prefix mm5; leaf-list x {type string; min-elements 5;}"
+ "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) adding \"min-elements\" property which already exists (with value \"5\").");
+ assert_null(lys_parse_mem(ctx, "module mm6 {namespace urn:mm6;prefix mm6; list x {config false; min-elements 5;}"
+ "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) adding \"min-elements\" property which already exists (with value \"5\").");
+ assert_null(lys_parse_mem(ctx, "module mm7 {namespace urn:mm7;prefix mm7; leaf-list x {type string; max-elements 5;}"
+ "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) adding \"max-elements\" property which already exists (with value \"5\").");
+ assert_null(lys_parse_mem(ctx, "module mm8 {namespace urn:mm8;prefix mm8; list x {config false; max-elements 5;}"
+ "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) adding \"max-elements\" property which already exists (with value \"5\").");
+ assert_null(lys_parse_mem(ctx, "module mm9 {namespace urn:mm9;prefix mm9; leaf-list x {type string;}"
+ "deviation /x {deviate replace {min-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) replacing with \"min-elements\" property \"1\" which is not present.");
+ assert_null(lys_parse_mem(ctx, "module mm10 {namespace urn:mm10;prefix mm10; list x {config false;}"
+ "deviation /x {deviate replace {min-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) replacing with \"min-elements\" property \"1\" which is not present.");
+ assert_null(lys_parse_mem(ctx, "module mm11 {namespace urn:mm11;prefix mm11; leaf-list x {type string;}"
+ "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) replacing with \"max-elements\" property \"1\" which is not present.");
+ assert_null(lys_parse_mem(ctx, "module mm12 {namespace urn:mm12;prefix mm12; list x {config false; }"
+ "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) replacing with \"max-elements\" property \"1\" which is not present.");
+
*state = NULL;
ly_ctx_destroy(ctx, NULL);
}