schema tree CHANGE reusing of latest revision schemas
It can be disabled with a CMake option.
diff --git a/src/context.c b/src/context.c
index 61cbba4..8a69e43 100644
--- a/src/context.c
+++ b/src/context.c
@@ -673,8 +673,8 @@
ly_ctx_load_sub_module(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
int implement, struct unres_schema *unres)
{
- const struct lys_module *mod;
- char *module_data;
+ struct lys_module *mod;
+ char *module_data = NULL;
int i;
void (*module_data_free)(void *module_data) = NULL;
LYS_INFORMAT format = LYS_IN_UNKNOWN;
@@ -689,52 +689,54 @@
}
}
}
- if (revision) {
- /* try to get the schema with the specific revision from the context,
- * include the disabled modules in the search to avoid their duplication,
- * they are enabled by the subsequent call to lys_set_implemented() */
- for (i = ctx->internal_module_count, mod = NULL; i < ctx->models.used; i++) {
- mod = ctx->models.list[i]; /* shortcut */
- if (ly_strequal(name, mod->name, 0) && mod->rev_size && !strcmp(revision, mod->rev[0].date)) {
+ /* try to get the schema from the context (with or without revision),
+ * include the disabled modules in the search to avoid their duplication,
+ * they are enabled by the subsequent call to lys_set_implemented() */
+ for (i = ctx->internal_module_count, mod = NULL; i < ctx->models.used; i++) {
+ mod = ctx->models.list[i]; /* shortcut */
+ if (ly_strequal(name, mod->name, 0)) {
+ if (revision && mod->rev_size && !strcmp(revision, mod->rev[0].date)) {
+ /* the specific revision was already loaded */
+ break;
+ } else if (!revision && mod->latest_revision) {
+ /* the latest revision of this module was already loaded */
break;
}
+ }
+ mod = NULL;
+ }
+ if (mod) {
+ /* module must be enabled */
+ if (mod->disabled) {
+ lys_set_enabled(mod);
+ }
+ /* module is supposed to be implemented */
+ if (implement && lys_set_implemented(mod)) {
+ /* the schema cannot be implemented */
mod = NULL;
}
- if (mod) {
- /* we get such a module, make it implemented */
- if (lys_set_implemented(mod)) {
- /* the schema cannot be implemented */
- mod = NULL;
- }
- return mod;
- }
+ return mod;
}
}
+ /* module is not yet in context, use the user callback or try to find the schema on our own */
if (ctx->imp_clb) {
+ ly_errno = LY_SUCCESS;
if (module) {
mod = lys_main_module(module);
module_data = ctx->imp_clb(mod->name, (mod->rev_size ? mod->rev[0].date : NULL), name, revision, ctx->imp_clb_data, &format, &module_data_free);
} else {
module_data = ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data, &format, &module_data_free);
}
- if (!module_data) {
- if (module || revision) {
- /* we already know that the specified revision is not present in context, and we have no other
- * option in case of submodules */
- LOGERR(LY_ESYS, "User module retrieval callback failed!");
- return NULL;
- } else {
- /* get the newest revision from the context */
- mod = ly_ctx_get_module_by(ctx, name, offsetof(struct lys_module, name), revision, 1, 0);
- if (mod && mod->disabled) {
- /* enable the required module */
- lys_set_enabled(mod);
- }
- return mod;
- }
+ if (!module_data && (ly_errno != LY_SUCCESS)) {
+ /* callback encountered an error, do not change it */
+ LOGERR(LY_SUCCESS, "User module retrieval callback failed!");
+ return NULL;
}
+ }
+ if (module_data) {
+ /* we got the module from the callback */
if (module) {
mod = (struct lys_module *)lys_sub_parse_mem(module, module_data, format, unres);
} else {
@@ -745,9 +747,17 @@
module_data_free(module_data);
}
} else {
+ /* module was not received from the callback or there is no callback set */
mod = lyp_search_file(ctx, module, name, revision, implement, unres);
}
+#ifdef LY_ENABLED_LATEST_REVISIONS
+ if (!revision && mod) {
+ /* module is the latest revision found */
+ mod->latest_revision = 1;
+ }
+#endif
+
return mod;
}
diff --git a/src/libyang.h.in b/src/libyang.h.in
index a8c4234..2ba604a 100644
--- a/src/libyang.h.in
+++ b/src/libyang.h.in
@@ -19,6 +19,8 @@
@ENABLE_CACHE_MACRO@
+@ENABLE_LATEST_REVISIONS_MACRO@
+
#include "tree_schema.h"
#include "tree_data.h"
#include "xml.h"
@@ -101,10 +103,10 @@
* where libyang will automatically search for schemas being imported or included. The search path
* can be later changed via ly_ctx_set_searchdir() and ly_ctx_unset_searchdrs() functions. If the search dir
* is specified, it is explored first. Except the searchpath, also all its subdirectories (and symlinks) are
- * taken into account. In case the module is not found, libyang tries to find the (sub)module also in current
+ * taken into account. In case the module is not found, libyang tries to find the (sub)module also in the current
* working working directory. Note, that in this case only the current directory without any other subdirectory
* is examined. This automatic searching can be completely avoided when the caller sets module searching callback
- * (#ly_module_imp_clb) via ly_ctx_set_module_imp_clb().
+ * (#ly_module_imp_clb) via ly_ctx_set_module_imp_clb(), but both approaches can also be combined.
*
* Schemas are added into the context using [parser functions](@ref howtoschemasparsers) - \b lys_parse_*().
* In case of schemas, also ly_ctx_load_module() can be used - in that case the #ly_module_imp_clb or automatic
@@ -122,11 +124,18 @@
* a module (and no other) as target of leafref, augment or deviation. All modules with deviation definition are always
* marked as implemented. The imported (not implemented) module can be set implemented by lys_set_implemented(). But
* the implemented module cannot be changed back to just imported module. The imported modules are used only as a
- * source of definitions for types (including identities) and uses statements. The data in such a modules are
- * ignored - caller is not allowed to create the data defined in the model via data parsers, the default nodes are
- * not added into any data tree and mandatory nodes are not checked in the data trees. This can be changed by
- * ly_ctx_set_allimplemented() function which causes that all the imported modules are automatically set to be
- * implemented.
+ * source of definitions for types and groupings for uses statements. The data in such modules are ignored - caller
+ * is not allowed to create the data (including instantiating identities) defined in the model via data parsers,
+ * the default nodes are not added into any data tree and mandatory nodes are not checked in the data trees. This
+ * can be changed by ly_ctx_set_allimplemented() function, which causes that all the imported modules are automatically
+ * set to be implemented.
+ *
+ * When loading/importing a module without revision, the latest revision of the required module is supposed to load.
+ * For a context, the first time the latest revision of a module is requested, it is properly searched for and loaded.
+ * However, when this module is requested (without revision) the second time, the one found previously is returned.
+ * This has the advantage of not searching for the module repeatedly but the drawback that if a later revision
+ * of the module is later made available, this context will not use it. If you are aware of a case when this
+ * optimization could cause problems, you can disable it using a cmake(1) build option (variable).
*
* Context holds all modules and their submodules internally. To get
* a specific module or submodule, use ly_ctx_get_module() and ly_ctx_get_submodule(). There are some additional
@@ -1291,7 +1300,9 @@
* @param[in] user_data User-supplied callback data.
* @param[out] format Format of the returned module data.
* @param[out] free_module_data Callback for freeing the returned module data. If not set, the data will be left untouched.
- * @return Requested module data or NULL if the callback is not able to provide the requested schema content for any reason.
+ * @return Requested module data, NULL if the module is supposed to be loaded
+ * using standard mechanisms (searched for in the filesystem), NULL and #ly_errno set if
+ * the callback failed resulting in the module failing to load.
*/
typedef char *(*ly_module_imp_clb)(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
void *user_data, LYS_INFORMAT *format, void (**free_module_data)(void *model_data));
@@ -1691,13 +1702,13 @@
* @ingroup logger
*/
typedef enum {
- LY_SUCCESS, /**< no error, not set by functions, included just to complete #LY_ERR enumeration */
- LY_EMEM, /**< Memory allocation failure */
- LY_ESYS, /**< System call failure */
- LY_EINVAL, /**< Invalid value */
- LY_EINT, /**< Internal error */
- LY_EVALID, /**< Validation failure */
- LY_EEXT /**< Extension error reported by an extension plugin */
+ LY_SUCCESS = 0, /**< no error, not set by functions, included just to complete #LY_ERR enumeration */
+ LY_EMEM, /**< Memory allocation failure */
+ LY_ESYS, /**< System call failure */
+ LY_EINVAL, /**< Invalid value */
+ LY_EINT, /**< Internal error */
+ LY_EVALID, /**< Validation failure */
+ LY_EEXT /**< Extension error reported by an extension plugin */
} LY_ERR;
/**
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 5ec9b6f..bf8d816 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -619,6 +619,10 @@
- 2 = deviation applied to this module are temporarily off */
uint8_t disabled:1; /**< flag if the module is disabled in the context */
uint8_t implemented:1; /**< flag if the module is implemented, not just imported */
+ uint8_t latest_revision:1; /**< flag if the module was loaded without specific revision and is
+ the latest revision found */
+ uint8_t padding1:7; /**< padding for 32b alignment */
+ uint8_t padding2[2];
/* array sizes */
uint8_t rev_size; /**< number of elements in #rev array */
@@ -629,8 +633,6 @@
uint16_t tpdf_size; /**< number of elements in #tpdf array */
uint8_t features_size; /**< number of elements in #features array */
- uint8_t padding[3]; /**< padding for 32b alignment */
-
uint8_t augment_size; /**< number of elements in #augment array */
uint8_t deviation_size; /**< number of elements in #deviation array */
uint8_t extensions_size; /**< number of elements in #extensions array */
@@ -681,6 +683,7 @@
- 2 = deviation applied to this module are temporarily off */
uint8_t disabled:1; /**< flag if the module is disabled in the context (same as in main module) */
uint8_t implemented:1; /**< flag if the module is implemented, not just imported (same as in main module) */
+ uint8_t padding[3]; /**< padding for 32b alignment */
/* array sizes */
uint8_t rev_size; /**< number of elements in #rev array */
@@ -691,8 +694,6 @@
uint16_t tpdf_size; /**< number of elements in #tpdf array */
uint8_t features_size; /**< number of elements in #features array */
- uint8_t padding[3]; /**< padding for 32b alignment */
-
uint8_t augment_size; /**< number of elements in #augment array */
uint8_t deviation_size; /**< number of elements in #deviation array */
uint8_t extensions_size; /**< number of elements in #extensions array */