data tree FEATURE lyd_new_path state list support
diff --git a/src/tree_data.c b/src/tree_data.c
index 2944077..931e070 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -1275,6 +1275,70 @@
return lyd_new_path2(parent, ctx, path, value, 0, options, node, NULL);
}
+static LY_ERR
+lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const char *value, uint32_t options)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ const struct lysc_node *schema;
+ struct ly_path_predicate *pred;
+ LY_ARRAY_COUNT_TYPE u, last_u;
+
+ assert(path);
+
+ /* go through all the compiled nodes */
+ LY_ARRAY_FOR(path, u) {
+ schema = path[u].node;
+ last_u = u;
+ if ((schema->nodetype == LYS_LIST) && (path[u].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ if (schema->flags & LYS_KEYLESS) {
+ /* creating a new keyless list instance */
+ break;
+ } else if ((u < LY_ARRAY_COUNT(path) - 1) || !(options & LYD_NEW_PATH_OPAQ)) {
+ LOG_LOCSET(schema, NULL, NULL, NULL);
+ LOGVAL(schema->module->ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path \"%s\".",
+ lys_nodetype2str(schema->nodetype), schema->name, str_path);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EINVAL;
+ } /* else creating an opaque list without keys */
+ }
+ }
+
+ if ((schema->nodetype == LYS_LEAFLIST) && (path[last_u].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ /* parse leafref value into a predicate, if not defined in the path */
+ if (!value) {
+ value = "";
+ }
+
+ r = LY_SUCCESS;
+ if (options & LYD_NEW_PATH_OPAQ) {
+ r = lys_value_validate(NULL, schema, value, strlen(value));
+ }
+ if (!r) {
+ /* store the new predicate */
+ path[last_u].pred_type = LY_PATH_PREDTYPE_LEAFLIST;
+ LY_ARRAY_NEW_GOTO(schema->module->ctx, path[last_u].predicates, pred, ret, cleanup);
+
+ ret = lyd_value_store(schema->module->ctx, &pred->value, ((struct lysc_node_leaflist *)schema)->type, value,
+ strlen(value), NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, schema, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ ++((struct lysc_type *)pred->value.realtype)->refcount;
+
+ if (schema->flags & LYS_CONFIG_R) {
+ /* creating a new state leaf-list instance */
+ --last_u;
+ }
+ } /* else we have opaq flag and the value is not valid, leave no predicate and then create an opaque node */
+ }
+
+ /* hide the nodes that should always be created so they are not found */
+ while (last_u + 1 < LY_ARRAY_COUNT(path)) {
+ LY_ARRAY_DECREMENT(path);
+ }
+
+cleanup:
+ return ret;
+}
+
API LY_ERR
lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent, struct lyd_node **new_node)
@@ -1284,8 +1348,7 @@
struct ly_path *p = NULL;
struct lyd_node *nparent = NULL, *nnode = NULL, *node = NULL, *cur_parent;
const struct lysc_node *schema;
- LY_ARRAY_COUNT_TYPE path_idx = 0;
- struct ly_path_predicate *pred;
+ LY_ARRAY_COUNT_TYPE path_idx = 0, orig_count;
LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
@@ -1302,54 +1365,28 @@
options & LYD_NEW_PATH_OUTPUT ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, LY_PREF_JSON,
NULL, NULL, &p), cleanup);
- assert(p);
- schema = p[LY_ARRAY_COUNT(p) - 1].node;
- if ((schema->nodetype == LYS_LIST) && (p[LY_ARRAY_COUNT(p) - 1].pred_type == LY_PATH_PREDTYPE_NONE) &&
- !(options & LYD_NEW_PATH_OPAQ)) {
- LOG_LOCSET(schema, NULL, NULL, NULL);
- LOGVAL(ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path \"%s\".",
- lys_nodetype2str(schema->nodetype), schema->name, path);
- LOG_LOCBACK(1, 0, 0, 0);
- ret = LY_EINVAL;
- goto cleanup;
- } else if ((schema->nodetype == LYS_LEAFLIST) && (p[LY_ARRAY_COUNT(p) - 1].pred_type == LY_PATH_PREDTYPE_NONE)) {
- /* parse leafref value into a predicate, if not defined in the path */
- if (!value) {
- value = "";
- }
-
- r = LY_SUCCESS;
- if (options & LYD_NEW_PATH_OPAQ) {
- r = lys_value_validate(NULL, schema, value, strlen(value));
- }
- if (!r) {
- /* store the new predicate */
- p[LY_ARRAY_COUNT(p) - 1].pred_type = LY_PATH_PREDTYPE_LEAFLIST;
- LY_ARRAY_NEW_GOTO(ctx, p[LY_ARRAY_COUNT(p) - 1].predicates, pred, ret, cleanup);
-
- ret = lyd_value_store(ctx, &pred->value, ((struct lysc_node_leaflist *)schema)->type, value, strlen(value),
- NULL, LY_PREF_JSON, NULL, LYD_HINT_DATA, schema, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- ++((struct lysc_type *)pred->value.realtype)->refcount;
- } /* else we have opaq flag and the value is not valid, leavne no predicate and then create an opaque node */
- }
+ /* check the compiled path before searching existing nodes, it may be shortened */
+ orig_count = LY_ARRAY_COUNT(p);
+ LY_CHECK_GOTO(ret = lyd_new_path_check_find_lypath(p, path, value, options), cleanup);
/* try to find any existing nodes in the path */
if (parent) {
ret = ly_path_eval_partial(p, parent, &path_idx, &node);
if (ret == LY_SUCCESS) {
- /* the node exists, are we supposed to update it or is it just a default? */
- if (!(options & LYD_NEW_PATH_UPDATE) && !(node->flags & LYD_DEFAULT)) {
- LOG_LOCSET(NULL, node, NULL, NULL);
- LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists", path);
- LOG_LOCBACK(0, 1, 0, 0);
- ret = LY_EEXIST;
- goto cleanup;
- }
+ if (orig_count == LY_ARRAY_COUNT(p)) {
+ /* the node exists, are we supposed to update it or is it just a default? */
+ if (!(options & LYD_NEW_PATH_UPDATE) && !(node->flags & LYD_DEFAULT)) {
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists", path);
+ LOG_LOCBACK(0, 1, 0, 0);
+ ret = LY_EEXIST;
+ goto cleanup;
+ }
- /* update the existing node */
- ret = lyd_new_path_update(node, value, value_type, &nparent, &nnode);
- goto cleanup;
+ /* update the existing node */
+ ret = lyd_new_path_update(node, value, value_type, &nparent, &nnode);
+ goto cleanup;
+ } /* else we were not searching for the whole path */
} else if (ret == LY_EINCOMPLETE) {
/* some nodes were found, adjust the iterator to the next segment */
++path_idx;
@@ -1364,6 +1401,11 @@
}
}
+ /* restore the full path for creating new nodes */
+ while (orig_count > LY_ARRAY_COUNT(p)) {
+ LY_ARRAY_INCREMENT(p);
+ }
+
/* create all the non-existing nodes in a loop */
for ( ; path_idx < LY_ARRAY_COUNT(p); ++path_idx) {
cur_parent = node;
@@ -1464,6 +1506,11 @@
cleanup:
lyxp_expr_free(ctx, exp);
+ if (p) {
+ while (orig_count > LY_ARRAY_COUNT(p)) {
+ LY_ARRAY_INCREMENT(p);
+ }
+ }
ly_path_free(ctx, p);
if (!ret) {
/* set out params only on success */
diff --git a/src/tree_data.h b/src/tree_data.h
index baf299e..56ff3fd 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -1051,6 +1051,9 @@
* and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
* and @p value is set, the predicate is preferred.
*
+ * For key-less lists and state leaf-lists, positional predicates can be used. If no preciate is used for these
+ * nodes, they are always created.
+ *
* @param[in] parent Data parent to add to/modify, can be NULL. Note that in case a first top-level sibling is used,
* it may no longer be first if @p path is absolute and starts with a non-existing top-level node inserted
* before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
@@ -1071,6 +1074,9 @@
* and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
* and @p value is set, the predicate is preferred.
*
+ * For key-less lists and state leaf-lists, positional predicates can be used. If no preciate is used for these
+ * nodes, they are always created.
+ *
* @param[in] parent Data parent to add to/modify, can be NULL. Note that in case a first top-level sibling is used,
* it may no longer be first if @p path is absolute and starts with a non-existing top-level node inserted
* before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.