data types FEATURE support for leafref data
TODO not yet fully tested, there is just a single basic test in unit
tests.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 6e15b60..e94e4f5 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -254,7 +254,7 @@
value = "";
value_len = 0;
}
- ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
+ ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic, 0, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
if (ret == LY_EINCOMPLETE) {
ly_set_add(&ctx->incomplete_type_validation, cur, LY_SET_OPT_USEASLIST);
} else if (ret) {
@@ -315,7 +315,7 @@
*tree = *result;
}
/* validate and store the value of the node */
- ret = lyd_value_parse(node, node->value.canonized, node->value.canonized ? strlen(node->value.canonized) : 0, 0,
+ ret = lyd_value_parse(node, node->value.canonized, node->value.canonized ? strlen(node->value.canonized) : 0, 0, 1,
lydxml_resolve_prefix, ctx, LYD_XML, trees);
LY_ARRAY_FREE(trees);
if (ret) {
diff --git a/src/plugins_types.c b/src/plugins_types.c
index d51fc43..bd80946 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -413,6 +413,10 @@
char *str;
struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
switch (type->basetype) {
case LY_TYPE_INT8:
LY_CHECK_RET(ly_type_parse_int("int16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, INT64_C(-128), INT64_C(127), value, value_len, &i, err));
@@ -472,6 +476,10 @@
struct lysc_type_num* type_num = (struct lysc_type_num*)type;
char *str;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
switch (type->basetype) {
case LY_TYPE_UINT8:
LY_CHECK_RET(ly_type_parse_uint("uint16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, UINT64_C(255), value, value_len, &u, err));
@@ -528,6 +536,10 @@
struct lysc_type_dec* type_dec = (struct lysc_type_dec*)type;
char buf[22];
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
if (!value || !value[0] || !value_len) {
*err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, strdup("Invalid empty decimal64 value."), NULL, NULL);
return LY_EVALID;
@@ -601,6 +613,10 @@
/* initiate */
*err = NULL;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
/* validate characters and remember the number of octets for length validation */
if (value_len) {
/* silently skip leading/trailing whitespaces */
@@ -693,6 +709,10 @@
{
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
/* length restriction of the string */
if (type_str->length) {
char buf[22];
@@ -745,6 +765,10 @@
size_t lws_count; /* leading whitespace count */
const char *can = NULL;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
/* remember the present items for further work */
items = ly_set_new();
LY_CHECK_RET(!items, LY_EMEM);
@@ -898,6 +922,10 @@
char *errmsg = NULL;
struct lysc_type_enum *type_enum = (struct lysc_type_enum*)type;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
/* find the matching enumeration value item */
LY_ARRAY_FOR(type_enum->enums, u) {
if (!strncmp(type_enum->enums[u].name, value, value_len) && type_enum->enums[u].name[value_len] == '\0') {
@@ -960,6 +988,10 @@
{
int8_t i;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
if (value_len == 4 && !strncmp(value, "true", 4)) {
i = 1;
} else if (value_len == 5 && !strncmp(value, "false", 5)) {
@@ -1002,6 +1034,10 @@
const void *UNUSED(context_node), struct lyd_node **UNUSED(trees),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
if (value_len) {
char *errmsg;
asprintf(&errmsg, "Invalid empty value \"%.*s\".", (int)value_len, value);
@@ -1063,6 +1099,10 @@
unsigned int u;
struct lysc_ident *ident;
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
+ return LY_SUCCESS;
+ }
+
/* locate prefix if any */
for (prefix_len = 0; prefix_len < value_len && value[prefix_len] != ':'; ++prefix_len);
if (prefix_len < value_len) {
@@ -1386,7 +1426,7 @@
/* init */
*err = NULL;
- if (!(options & LY_TYPE_OPTS_INCOMPLETE_DATA) && ((struct lyd_node_term*)context_node)->value.target) {
+ if (options & LY_TYPE_OPTS_SECOND_CALL) {
/* the second run, the first one ended with LY_EINCOMPLETE, but we have prepared the target structure */
if (!lyd_target(((struct lyd_node_term*)context_node)->value.target, trees)) {
@@ -1759,6 +1799,257 @@
ly_type_free_canonical(ctx, type, value);
}
+/**
+ * @brief Validate, canonize and store value of the YANG built-in leafref type.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+ const void *context_node, struct lyd_node **trees,
+ struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ unsigned int u;
+ char *errmsg = NULL;
+ struct lysc_type_leafref *type_lr = (struct lysc_type_leafref*)type;
+ int storage_dummy = 0;
+ const char *first_pred = NULL;
+ const struct lyd_node *start_search;
+
+ if (!(options & (LY_TYPE_OPTS_STORE | LY_TYPE_OPTS_INCOMPLETE_DATA)) && type_lr->require_instance) {
+ /* if there is no storage, but we will check the instance presence in data tree(s),
+ * we need some (dummy) storage for data comparison */
+ storage = calloc(1, sizeof *storage);
+ storage_dummy = 1;
+ }
+ /* rewrite leafref plugin stored in the storage by default */
+ storage->plugin = type_lr->realtype->plugin;
+
+ /* check value according to the real type of the leafref target */
+ ret = type_lr->realtype->plugin->store(ctx, type_lr->realtype, value, value_len, options,
+ get_prefix, parser, format, context_node, trees,
+ storage, canonized, err);
+ if (ret != LY_SUCCESS && ret != LY_EINCOMPLETE) {
+ return ret;
+ }
+
+ if (type_lr->require_instance) {
+ if (options & LY_TYPE_OPTS_INCOMPLETE_DATA) {
+ return LY_EINCOMPLETE;
+ }
+
+ /* find corresponding data instance */
+ const char *token = type_lr->path;
+ const struct lyd_node *node;
+ struct lys_module *context_mod = ((const struct lyd_node*)context_node)->schema->module;
+
+ if (token[0] == '/') {
+ /* absolute-path */
+ node = NULL;
+ } else {
+ /*relative-path */
+ node = (const struct lyd_node*)context_node;
+ }
+
+ /* resolve leafref path */
+ while (*token) {
+ if (!strcmp(token, "../")) {
+ /* level up */
+ token += 2;
+ node = (struct lyd_node*)node->parent;
+ } else if (!strcmp(token, "/../")) {
+ /* level up */
+ token += 3;
+ node = (struct lyd_node*)node->parent;
+ } else if (*token == '/') {
+ /* level down */
+ const char *prefix, *id;
+ size_t prefix_len, id_len;
+ const struct lys_module *mod;
+
+ /* reset predicates */
+ first_pred = NULL;
+
+ token++;
+ ly_parse_nodeid(&token, &prefix, &prefix_len, &id, &id_len);
+ mod = lys_module_find_prefix(context_mod, prefix, prefix_len);
+
+ if (node) {
+ /* inner node */
+ start_search = lyd_node_children(node);
+next_instance_inner:
+ if (start_search) {
+ node = lyd_search(start_search, mod, id, id_len, 0, NULL, 0);
+ } else {
+ node = NULL;
+ }
+ } else {
+ /* top-level node */
+ LY_ARRAY_FOR(trees, u) {
+ start_search = trees[u];
+next_instance_toplevel:
+ node = lyd_search(start_search, mod, id, id_len, 0, NULL, 0);
+ if (node) {
+ break;
+ }
+ }
+ }
+ if (!node) {
+ /* node not found */
+ asprintf(&errmsg, "Invalid leafref - required instance \"%.*s\" does not exists in the data tree(s).",
+ (int)(token - type_lr->path), type_lr->path);
+ goto error;
+ }
+ } else if (*token == '[') {
+ /* predicate */
+ const char *pred_start = token;
+ const struct lyd_node_term *key;
+ const struct lyd_node *value;
+ const struct lys_module *mod;
+ const char *pred_end = ly_type_store_instanceid_predicate_end(token);
+ const char *src_prefix, *src;
+ size_t src_prefix_len, src_len;
+
+ /* remember start of the first predicate to be able to return back when comparison fails
+ * on a subsequent predicate in case of multiple predicates - on the next node instance
+ * we have to start again with the first predicate */
+ if (!first_pred) {
+ first_pred = pred_start;
+ }
+
+ /* move after "[ *WSP" */
+ token++;
+ for (; isspace(*token); token++);
+
+ /* parse node-identifier */
+ ly_parse_nodeid(&token, &src_prefix, &src_prefix_len, &src, &src_len);
+ mod = lys_module_find_prefix(context_mod, src_prefix, src_prefix_len);
+
+ key = (const struct lyd_node_term*)lyd_search(lyd_node_children(node), mod, src, src_len, LYS_LEAF, NULL, 0);
+ if (!key) {
+ LOGINT(ctx);
+ goto error;
+ }
+
+ /* move after "*WSP = *WSP" */
+ for (; *token != '='; token++);
+ for (token++; isspace(*token); token++);
+ /* move after "current() *WSP / *WSP 1*(.. *WSP /)" */
+ token += 8;
+ for (; *token != '/'; token++);
+ for (; *token != '.'; token++);
+ value = (struct lyd_node*)node->parent; /* level up by .. */
+ for (token += 2; *token != '/'; token++);
+
+ /* parse "*WSP *(node-identifier *WSP / *WSP) node-identifier */
+ do {
+ for (token++; isspace(*token); token++);
+
+ /* parse node-identifier */
+ ly_parse_nodeid(&token, &src_prefix, &src_prefix_len, &src, &src_len);
+ mod = lys_module_find_prefix(context_mod, src_prefix, src_prefix_len);
+
+ if (!value) {
+ /* top-level search */
+ LY_ARRAY_FOR(trees, u) {
+ value = lyd_search(trees[u], mod, src, src_len, 0, NULL, 0);
+ if (value) {
+ break;
+ }
+ }
+ } else {
+ /* inner node */
+ value = lyd_search(lyd_node_children(value), mod, src, src_len, 0, NULL, 0);
+ }
+ if (!value) {
+ /* node not found - try another instance */
+ goto next_instance;
+ }
+
+ for (; isspace(*token); token++);
+ } while (*token == '/');
+
+ /* compare key and the value */
+ if (key->value.plugin->compare(&key->value, &((struct lyd_node_term*)value)->value)) {
+ /* nodes does not match, try another instance */
+next_instance:
+ token = first_pred;
+ if (node->parent) {
+ goto next_instance_inner;
+ } else {
+ goto next_instance_toplevel;
+ }
+ }
+ /* match */
+
+ /* move after predicate */
+ assert(token == pred_end);
+ token = pred_end + 1;
+ }
+ }
+
+ /* check value */
+ while (node && type_lr->realtype->plugin->compare(&((struct lyd_node_term*)node)->value, storage)) {
+ /* values do not match, try another instance of the node */
+ const struct lysc_node *schema = node->schema;
+ LY_LIST_FOR(node->next, node) {
+ if (node->schema == schema) {
+ break;
+ }
+ }
+ }
+ if (!node) {
+ /* node not found */
+ asprintf(&errmsg, "Invalid leafref value \"%.*s\" - required instance \"%.*s\" with this value does not exists in the data tree(s).",
+ (int)value_len, value, (int)(token - type_lr->path), type_lr->path);
+ goto error;
+ }
+ }
+
+ if (storage_dummy) {
+ storage->plugin->free(ctx, type_lr->realtype, storage);
+ free(storage);
+ }
+ return ret;
+
+error:
+ if (!*err) {
+ *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+ }
+ if (storage_dummy) {
+ storage->plugin->free(ctx, type_lr->realtype, storage);
+ free(storage);
+ }
+ return LY_EVALID;
+
+}
+
+/**
+ * @brief Comparison callback checking the leafref value.
+ *
+ * Implementation of the ly_type_compare_clb.
+ */
+static LY_ERR
+ly_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ return val1->plugin->compare(val1, val2);
+}
+
+/**
+ * @brief Free value of the YANG built-in leafref type.
+ *
+ * Implementation of the ly_type_free_clb.
+ */
+static void
+ly_type_free_leafref(struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value *value)
+{
+ struct lysc_type *realtype = ((struct lysc_type_leafref*)type)->realtype;
+
+ realtype->plugin->free(ctx, realtype, value);
+}
+
struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
{0}, /* LY_TYPE_UNKNOWN */
{.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
@@ -1774,7 +2065,7 @@
{.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
{.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .free = ly_type_free_canonical},
{.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .free = ly_type_free_instanceid},
- {0}, /* TODO LY_TYPE_LEAFREF */
+ {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref, .free = ly_type_free_leafref},
{0}, /* TODO LY_TYPE_UNION */
{.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
{.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
diff --git a/src/plugins_types.h b/src/plugins_types.h
index d1e5b87..e3c2356 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -64,6 +64,8 @@
can (e.g. store the canonical/auxiliary value if it is requested) and in the case of need to use
data trees (checking require-instance), it returns LY_EINCOMPLETE.
Caller is supposed to call such validation callback again later with complete data trees. */
+#define LY_TYPE_OPTS_SECOND_CALL 0x20 /**< Flag for the second call of the callback when the first call returns LY_EINCOMPLETE,
+ other options should be the same as for the first call. */
/**
* @}
diff --git a/src/tree_data.c b/src/tree_data.c
index c186930..d3ea3d2 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -89,14 +89,15 @@
}
LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic, int second,
ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, struct lyd_node **trees)
{
LY_ERR ret = LY_SUCCESS, rc;
struct ly_err_item *err = NULL;
struct ly_ctx *ctx;
struct lysc_type *type;
- int options = LY_TYPE_OPTS_STORE | (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+ int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
+ (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
assert(node);
ctx = node->schema->module->ctx;
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 668f9a0..a9ff2e6 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -45,6 +45,7 @@
* @param[in] value String value to be parsed, must not be NULL.
* @param[in] value_len Length of the give @p value (mandatory).
* @param[in] dynamic Flag if @p value is a dynamically allocated memory and should be directly consumed/freed inside the function.
+ * @param[in] second Flag for the second call after returning LY_EINCOMPLETE
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] parser Parser's data for @p get_prefix
* @param[in] format Input format of the data.
@@ -55,7 +56,7 @@
* @return LY_EINCOMPLETE in case the @p trees is not provided and it was needed to finish the validation.
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
+LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic, int second,
ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,struct lyd_node **trees);
/**
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index 59eee4a..485ad09 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -904,18 +904,21 @@
struct lyd_node *tree;
struct lyd_node_term *leaf;
- const char *data = "<leaflisttarget xmlns=\"urn:tests:types\">x</leaflisttarget><leaflisttarget xmlns=\"urn:tests:types\">y</leaflisttarget>"
- "<lref xmlns=\"urn:tests:types\">y</lref>";
+ const char *data = "<leaflisttarget xmlns=\"urn:tests:types\">x</leaflisttarget><leaflisttarget xmlns=\"urn:tests:types\">ddd</leaflisttarget>"
+ "<lref xmlns=\"urn:tests:types\">xx</lref>";
+ assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ logbuf_assert("Invalid leafref value \"xx\" - required instance \"/leaflisttarget\" with this value does not exists in the data tree(s). /");
+#if 0
/* valid data */
assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
tree = tree->prev;
assert_int_equal(LYS_LEAF, tree->schema->nodetype);
assert_string_equal("lref", tree->schema->name);
leaf = (struct lyd_node_term*)tree;
- assert_string_equal("y", leaf->value.canonized);
+ assert_string_equal("xx", leaf->value.canonized);
lyd_free_all(tree);
-
+#endif
/* invalid value */
data = "<leaflisttarget xmlns=\"urn:tests:types\">x</leaflisttarget>"
"<lref xmlns=\"urn:tests:types\">y</lref>";