schema compile CHANGE support for deviation of mandatory property
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 1c23753..376bab4 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -567,6 +567,7 @@
* 6 LYS_MAND_TRUE |x|x|x|x|x|x| | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 7 LYS_ORDBY_USER | | | |x|x| | | | | | | | | |
+ * LYS_MAND_FALSE | |x|x| | |x| | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
* LYS_PRESENCE |x| | | | | | | | | | | | | |
@@ -601,7 +602,9 @@
The ::lysc_node_leaflist and ::lysc_node_leaflist have this flag in case that min-elements > 0.
The ::lysc_node_container has this flag if it is not a presence container and it has at least one
child with LYS_MAND_TRUE. */
-#define LYS_MAND_FALSE 0x40 /**< mandatory false; applicable only to ::lysp_node_choice, ::lysp_node_leaf and ::lysp_node_anydata */
+#define LYS_MAND_FALSE 0x40 /**< mandatory false; applicable only to ::lysp_node_choice/::lysc_node_choice,
+ ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata.
+ This flag is present only in case the mandatory false statement was explicitly specified. */
#define LYS_MAND_MASK 0x60 /**< mask for mandatory values */
#define LYS_PRESENCE 0x80 /**< flag for presence property of a container, applicable only to ::lysc_node_container */
#define LYS_UNIQUE 0x80 /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
@@ -635,7 +638,7 @@
away when it is refined to be mandatory node. Similarly it is used for deviations to distinguish
between own default or the default values taken from the type. */
#define LYS_SET_UNITS 0x0400 /**< flag to know if the leaf's/leaflist's units are their own (flag set) or it is taken from the type. */
-#define LYS_SET_CONFIG 0x0800 /**< flag to know if the config property set explicitely (flag set) or it is inherited. */
+#define LYS_SET_CONFIG 0x0800 /**< flag to know if the config property was set explicitly (flag set) or it is inherited. */
#define LYS_FLAGS_COMPILED_MASK 0xff /**< mask for flags that maps to the compiled structures */
/** @} */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index e04e409..06054a6 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -3864,7 +3864,7 @@
} else { /* unset flag */
for (; parent && parent->nodetype == LYS_CONTAINER && (parent->flags & LYS_MAND_TRUE); parent = parent->parent) {
for (iter = (struct lysc_node*)lysc_node_children(parent); iter; iter = iter->next) {
- if (iter->flags && LYS_MAND_TRUE) {
+ if (iter->flags & LYS_MAND_TRUE) {
/* there is another mandatory node */
return;
}
@@ -4065,6 +4065,68 @@
}
/**
+ * @brief Apply refined or deviated mandatory flag to the target node.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Target node where the mandatory property is supposed to be changed.
+ * @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).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_change_mandatory(struct lysc_ctx *ctx, struct lysc_node *node, uint16_t mandatory_flag, const char *nodeid, int refine_flag)
+{
+ if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid %s of mandatory in \"%s\" - %s cannot hold mandatory statement.",
+ refine_flag ? "refine" : "deviation", nodeid, lys_nodetype2str(node->nodetype));
+ return LY_EVALID;
+ }
+
+ if (mandatory_flag & LYS_MAND_TRUE) {
+ /* 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;
+ } 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 (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);
+ return LY_EVALID;
+ }
+
+ node->flags &= ~LYS_MAND_FALSE;
+ node->flags |= LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 1);
+ } else {
+ /* make mandatory false */
+ node->flags &= ~LYS_MAND_TRUE;
+ node->flags |= LYS_MAND_FALSE;
+ lys_compile_mandatory_parents(node->parent, 0);
+ if ((node->nodetype & LYS_LEAF) && !((struct lysc_node_leaf*)node)->dflt) {
+ /* get the type's default value if any */
+ DUP_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->type->dflt, ((struct lysc_node_leaf*)node)->dflt);
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
* @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
* If present, also apply uses's modificators.
*
@@ -4289,48 +4351,7 @@
/* mandatory */
if (rfn->flags & LYS_MAND_MASK) {
- if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid refine of mandatory in \"%s\" - %s cannot hold mandatory statement.",
- rfn->nodeid, lys_nodetype2str(node->nodetype));
- goto error;
- }
- /* in compiled flags, only the LYS_MAND_TRUE is present */
- if (rfn->flags & LYS_MAND_TRUE) {
- /* 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 refine of mandatory in \"%s\" - leaf already has \"default\" statement.", rfn->nodeid);
- goto error;
- } 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 refine of mandatory in \"%s\" - choice already has \"default\" statement.", rfn->nodeid);
- goto error;
- }
- if (node->parent && (node->parent->flags & LYS_SET_DFLT)) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid refine of mandatory in \"%s\" - %s under the default case.",
- rfn->nodeid, lys_nodetype2str(node->nodetype));
- goto error;
- }
-
- node->flags |= LYS_MAND_TRUE;
- lys_compile_mandatory_parents(node->parent, 1);
- } else {
- /* make mandatory false */
- node->flags &= ~LYS_MAND_TRUE;
- lys_compile_mandatory_parents(node->parent, 0);
- if ((node->nodetype & LYS_LEAF) && !((struct lysc_node_leaf*)node)->dflt) {
- /* get the type's default value if any */
- DUP_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->type->dflt, ((struct lysc_node_leaf*)node)->dflt);
- }
- }
+ LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, node, rfn->flags, rfn->nodeid, 1), error);
}
/* presence */
@@ -4990,6 +5011,15 @@
}
/* [mandatory-stmt] */
+ if (d_add->flags & LYS_MAND_MASK) {
+ if (devs[u]->target->flags & LYS_MAND_MASK) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid deviation (%s) adding \"mandatory\" property which already exists (with value \"mandatory %s\").",
+ devs[u]->nodeid, devs[u]->target->flags & LYS_MAND_TRUE ? "true" : "false");
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_add->flags, devs[u]->nodeid, 0), cleanup);
+ }
/* [min-elements-stmt] */
@@ -5182,6 +5212,14 @@
}
/* [mandatory-stmt] */
+ if (d_rpl->flags & LYS_MAND_MASK) {
+ if (!(devs[u]->target->flags & LYS_MAND_MASK)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, devs[u]->nodeid,
+ "replacing", "mandatory", d_rpl->flags & LYS_MAND_TRUE ? "mandatory true" : "mandatory false");
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_rpl->flags, devs[u]->nodeid, 0), cleanup);
+ }
/* [min-elements-stmt] */
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 1edd22f..4355725 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -2334,7 +2334,7 @@
assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;import grp {prefix g;}"
"uses g:grp {refine c/ch/a/a {mandatory true;}}}", LYS_IN_YANG));
- logbuf_assert("Invalid refine of mandatory in \"c/ch/a/a\" - leaf under the default case.");
+ logbuf_assert("Invalid refine of mandatory in \"c/ch/a/a\" under the default case.");
assert_null(lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;import grp {prefix g;}"
"uses g:grp {refine c/x {default hello;}}}", LYS_IN_YANG));
@@ -2684,6 +2684,20 @@
assert_string_equal("x", node->name);
assert_true(node->flags & LYS_CONFIG_W);
+ assert_non_null(mod = lys_parse_mem(ctx, "module m {namespace urn:m;prefix m;"
+ "container a {leaf a {type string;}}"
+ "container b {leaf b {mandatory true; type string;}}"
+ "deviation /a/a {deviate add {mandatory true;}}"
+ "deviation /b/b {deviate replace {mandatory false;}}}", LYS_IN_YANG));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal("a", node->name);
+ assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+ assert_true((lysc_node_children(node)->flags & LYS_MAND_MASK) == LYS_MAND_TRUE);
+ assert_non_null(node = node->next);
+ assert_string_equal("b", node->name);
+ 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_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.");
@@ -2791,6 +2805,22 @@
assert_null(lys_parse_mem(ctx, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}"
"deviation /top {deviate add {config false;}}}", LYS_IN_YANG));
logbuf_assert("Invalid deviation of config in \"/top\" - configuration node cannot be child of any state data node.");
+ assert_null(lys_parse_mem(ctx, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}"
+ "deviation /x {deviate add {config true;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) adding \"config\" property which already exists (with value \"config false\").");
+
+ assert_null(lys_parse_mem(ctx, "module kk1 {namespace urn:kk1;prefix kk1; container top {leaf a{type string;}}"
+ "deviation /top {deviate add {mandatory true;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation of mandatory in \"/top\" - container cannot hold mandatory statement.");
+ assert_null(lys_parse_mem(ctx, "module kk2 {namespace urn:kk2;prefix kk2; container top {leaf a{type string;}}"
+ "deviation /top {deviate replace {mandatory true;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/top) replacing \"mandatory\" property \"mandatory true\" which is not present.");
+ assert_null(lys_parse_mem(ctx, "module kk3 {namespace urn:kk3;prefix kk3; container top {leaf x {type string;}}"
+ "deviation /top/x {deviate replace {mandatory true;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/top/x) replacing \"mandatory\" property \"mandatory true\" which is not present.");
+ assert_null(lys_parse_mem(ctx, "module kk4 {namespace urn:kk4;prefix kk4; leaf x {mandatory true; type string;}"
+ "deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid deviation (/x) adding \"mandatory\" property which already exists (with value \"mandatory true\").");
*state = NULL;
ly_ctx_destroy(ctx, NULL);