/** @file store.h * * @brief Generic store dependency. * * This code allows to use the store interface with any supported back end * without any knowledge of the back end implementations. Code using the store * interface need only include this file. * * New store implementations should be added to this file as `#include`s as * well as entries in the `BACKEND_TBL` macro. */ #ifndef _LSUP_STORE_H #define _LSUP_STORE_H /* * Add new back end headers here. */ #include "lsup/store_htable.h" #include "lsup/store_mdb.h" /** @defgroup store Backend store module * @ingroup public * @{ */ /* * Define backend types. * * Add new store implementations to this table. */ #define BACKEND_TBL \ /* #enum suffix#store if */\ ENTRY( HTABLE, htstore_int ) \ ENTRY( MDB, mdbstore_int ) \ /// Store types. All prefixed with `LSUP_STORE_`. typedef enum { #define ENTRY(a, b) \ LSUP_STORE_##a, BACKEND_TBL #undef ENTRY } LSUP_StoreType; /** @brief Store structure. * * Code using the store interface should create an instance of this structure * with pointers to the store interface of their choice, and to an opaque * structure to hold the store state. * * Iterator state handles generated by lookup and add functions are kept * outside of this structure, but manipulated by the interface specified in * here. * * @sa #LSUP_graph_new() */ typedef struct store_t { LSUP_StoreType type; ///< Store type. char * id; /**< Store ID. * * NOTE: This is * NULL for volatile stores. */ const LSUP_StoreInt * sif; ///< Store interface. void * data; ///< Store back end data. } LSUP_Store; /** @brief Return store interface for a specific type. */ const LSUP_StoreInt * LSUP_store_int (LSUP_StoreType type); /** @brief Return the store type label. * * @param type Store type enum. * * @return Store type label as `STORE_`. */ const char * LSUP_store_type_label (LSUP_StoreType type); /** @brief Create a new store. * * The life cycle of a store normally outspans the one of one or multiple * graphs with the same back end, hence it is managed independently. * * @param[in] store_type Type of store backing the graph. One of the values of * #LSUP_StoreType. * * @param[in] store_id Identifier for the store. This may be * interpreted differently by each store implementation. For the MDB store, * this is the file path where the store is located. It is ignored by volatile * stores (with LSUP_STORE_PERM feature flag set to false). If a store * does not exist for the given identifier, a new one is initialized. If this * parameter is NULL, the default store for the selected type is used. * * @param[in] size Initial size of the store. Only used for optimization * purposes. It may be ignored by some implementations and it is always safe * to set to 0. * * @param clear Flag that is passed to the `init_fn` function of the store * interface, if present, to clear an existing store with the same ID. * * @return Store handle. It must be freed with #LSUP_store_free(). */ LSUP_Store * LSUP_store_new ( const LSUP_StoreType store_type, const char *store_id, size_t size, bool clear); /** @brief Free a store created with #LSUP_store_new(). * * @param[in] store Store handle. */ void LSUP_store_free (LSUP_Store *store); size_t LSUP_store_size (const LSUP_Store *store); /// Feature flags belonging to the store interface. const LSUP_StoreFeature LSUP_store_features (const LSUP_Store *store); /// Store identifier. char * LSUP_store_id (const LSUP_Store *store); /** @brief Begin a transaction. * * If the store supports it, begin a transaction. Only one transaction may be * opened at a time. * * The transaction must be either committed with #LSUP_store_commit() or * rolled back with #LSUP_store_abort(). * * @param[in] store Store handle. * * @param[in] flags Unused for now, use 0. TODO * * @param[out] txn Address to be populated with the new transaction handle. * * @return LSUP_OK on success; LSUP_VALUE_ERR if the store does not * support transactions; LSUP_TXN_ERR if the store has already an uncommitted * transaction; <0 on other errors. */ LSUP_rc LSUP_store_begin (LSUP_Store *store, int flags, void **txn); /** @brief Commit a transaction. * * If the store supports it, commit an open transaction. In case of * error, the transaction is left open and it is advisable to roll it back with * #LSUP_store_abort(). * * @param[in] store Store handle. * * @param[in] txn transaction handle to commit. * * @return LSUP_OK if the transaction was committed successfully; LSUP_NOACTION * if NULL was passed; LSUP_TXN_ERR on error. */ LSUP_rc LSUP_store_commit (LSUP_Store *store, void *txn); /** @brief Abort (roll back) a transaction. * * If the store supports it, abort an open transaction and abandon all changes. * * @param[in] store Store handle. * * @param[in] txn transaction handle to abort. */ void LSUP_store_abort (LSUP_Store *store, void *txn); /** @brief Update the context of triples in a context-aware store. * * When a context URI is updated, all relationships of triples to that context * must be updated to reflect the new context. * * @param[in] store Store handle. * * @param[in] old_c Old context handle. * * @param[in] new_c New context handle. * * @param[in] udata Implementation-specific context data. * * @return LSUP_OK on success; LSUP_NOACTION if no triples were found under the * given context; <0 on error. */ LSUP_rc LSUP_store_update_ctx_txn ( LSUP_Store *store, void *txn, const LSUP_Buffer *old_c, const LSUP_Buffer *new_c); #define LSUP_store_update_ctx(store, ...) \ LSUP_store_update_txn (store, NULL, __VA_ARGS__) /** @brief Initialize bulk triple load. * * This is the first step of a bulk load. It is best used when the data at hand * need to be pre-processed, which can be done in the same loop as the next * step to keep memory usage low. * * @param[in] store The store to add to. * * @param[in] sc Context as a serialized term. If this is NULL, and the * default context is not NULL, triples will be added to the default context * for the store, If the default context for the store is NULL, regardless of * the value of sc, triples will be added with no context. Only meaningful * for stores with the LSUP_STORE_CTX feature. * * @param[in] udata User data. Consult individual store implementations for * how this is interpreted. * * @return Iterator handle to be passed to the following load steps. */ void * LSUP_store_add_init_txn ( LSUP_Store *store, void *txn, const LSUP_Buffer *sc); /// Non-transactional version of #LSUP_store_add_init_txn(). #define LSUP_store_add_init(store, ...) \ LSUP_store_add_init_txn (store, NULL, __VA_ARGS__) /** @brief Add one triple into the store. * * This must be called after #store_add_init_fn_t, using the iterator * yielded by that function. It may be called multiple times and must be * followed by #store_add_done_fn_t or #store_add_abort_fn_t (if supported). * * @param[in] it Iterator obtained by #store_add_init_fn_t. * The following members are of interest: * it->i stores the total number of records inserted. * * @param[in] sspo Serialized triple to be added. * * @return LSUP_OK if the triple was inserted; LSUP_NOACTION if the triple * already existed; LSUP_DB_ERR if an MDB error occurred. */ LSUP_rc LSUP_store_add_iter ( LSUP_Store *store, void *it, const LSUP_BufferTriple *sspo); /** @brief Abort an add loop and free iterator. * * Usually called on an irrecoverable error from #store_add_iter_fn_t. None of * the successful inserts in the same loop is retained. * * @param[in] store Store handle. * * @param[in] it Iterator obtained by #store_add_init_fn_t. */ void LSUP_store_add_abort (LSUP_Store *store, void *it); /** @brief Finalize an add loop and free iterator. * * This must be called after #store_add_iter_fn_t. * * @param[in] store Store handle. * * @param[in] it Iterator obtained by #store_add_init_fn_t. */ LSUP_rc LSUP_store_add_done (LSUP_Store *store, void *it); /** @brief Add a single term to the store. * * @param[in] store Store handle. * * @param[in] sterm Serialized term to store. * * @param[in] udata Implementation-defined data. See individual * implementations' documentation. */ LSUP_rc LSUP_store_add_term_txn (LSUP_Store *store, void *txn, LSUP_Buffer *sterm); /// Non-transactional version of #LSUP_store_add_term_txn(). #define LSUP_store_add_term(store, ...) \ LSUP_store_add_term_txn (store, NULL, __VA_ARGS__) /** @brief Look up triples by pattern matching. * * This function may return a count of matches and/or an iterator of results as * serialized triples. * * For stores with #LSUP_STORE_TXN, this opens a read-only transaction. The * transaction handle is held in the iterator structure and is closed when the * iterator is freed with #iter_free_fn_t(). * * Any and all of the terms may be NULL, which indicates an unbound query * term. Stores witout context support will always ignore sc. * * @param[in] store The store to be queried. * * @param[in] ss Serialized s term. * * @param[in] sp Serialized p term. * * @param[in] so Serialized o term. * * @param[in] sc Serialized context to limit search to. It may be NULL, in * which case search is done in all contexts. Note that triples inserted * without context are assigned the *default* context for the store. * * @param[in] udata User data. Consult individual store implementations for * how this is interpreted. * * @param[out] ct If not NULL, this will be populated with the number of * entries found. In some implementations, it is very inexpensive to set for * lookups without context, much less so for 1-bound and 2-bound context * lookups, in which cases it should be set only if needed. * * @return Iterator handle that will be populated with a result iterator. This * is always created even if no matches are found and must be freed with * #iter_free_fn_t() after use. If matches are found, the iterator points to * the first result which can be retrieved with #iter_next_fn_t. */ void * LSUP_store_lookup_txn ( const LSUP_Store *store, void *txn, const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so, const LSUP_Buffer *sc, size_t *ct); /// Non-transactional version of #LSUP_store_lookup_txn(). #define LSUP_store_lookup(store, ...) \ LSUP_store_lookup_txn (store, NULL, __VA_ARGS__) /** @brief Yield the matching triples and advance the iterator. * * @note Iterators keep transactions open. Don't hold on to them longer than * necessary. * * @note If the store interface has the LSUP_STORE_COW feature, the memory * buffer referenced by the #LSUP_Buffer handle is owned by the database. It * must not be written to or freed. To modify the data or use them beyond the * caller's scope, this memory must be copied. Note that the #LSUP_Buffer * handle must still be freed (with a plain `free()`), but not the underlying * data buffer, since only the latter is owned by the back end. Implementations * with the `LSUP_STORE_COW` feature SHOULD create buffers with the * `LSUP_BUF_BORROWED` flag, so that they can be safely freed with * #LSUP_buffer_free() and #LSUP_btriple_free(). * * @param[in] it Opaque iterator handle obtained with #store_lookup_fn_t. * * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized * terms if found. It may be NULL, in which case it is not populated. * * @param[out] ctx If not NULL, it is populated with a NULL-terminated array of * LSUP_Buffer structs, one for each context associated with the matching * triple. These contexts are the same regardless of the context filter used * in the lookup. The array is freed with a simple `free()`. This parameter * is ignored by implementations without the LSUP_STORE_CTX feature. * * To iterate over the context array, use this loop: * * size_t i = 0; * while (ctx[i].addr) * do_something(ctx + (i++)); * * @return LSUP_OK if results were found; LSUP_END if no (more) results were * found; LSUP_DB_ERR if a backend error occurred. */ LSUP_rc LSUP_store_iter_next ( LSUP_Store *store, void *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx); /** @brief Free an iterator allocated by a lookup. * * @param[in] it Iterator pointer. It will be set to NULL after freeing. */ void LSUP_store_iter_free (LSUP_Store *store, void *it); /** @brief Get iterator active transaction handle. * * This function is used to get an active transaction during an iteration loop * in order to perform an action using the store state within that loop. Some * stores (e.g. MDB) only support one R/W open transaction per thread, so this * is also the only way to perform anything else than iterating or committing * while a loop is open. * * @param[in] it Iterator handle to get the transaction from. * * @return Transaction handle. DO NOT close this transaction directly. */ void * LSUP_store_iter_txn (const LSUP_Store *store, void *it); /** @brief Delete triples by pattern matching. * * The ss, sp, so, sc terms act as a matching pattern as documented in * @sa #store_lookup_fn_t(). if not NULL, ct yields the number of triples * actually deleted. * * @param[in] udata User data. Consult individual store implementations for * how this is interpreted. */ LSUP_rc LSUP_store_remove_txn ( LSUP_Store *store, void *txn, const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so, const LSUP_Buffer *sc, size_t *ct); /// Non-transactional version of #LSUP_store_remove_txn(). #define LSUP_store_remove(store, ...) \ LSUP_store_remove_txn (store, NULL, __VA_ARGS__) /** @brief get index of all graph (context) URIs in a store. * * Only applicable to stores with the LSUP_STORE_CTX feature flag. * * @param[in] store Store handle. * * @return Set of all context URIs. */ LSUP_Buffer ** LSUP_store_ctx_list_txn (LSUP_Store *store, void *txn); /// Non-transactional version of #LSUP_store_ctx_list_txn(). #define LSUP_store_ctx_list(store) \ LSUP_store_ctx_list_txn (store, NULL) /// @} END store #endif /* LSUP_STORE_H */