/** @file store_interface.h * * @brief Common store back end interfaces. * * Code using the store interface should include NOT this header, but rahter * `store.h`. * * This header is included by all back end implementations, which are in * their turn included by `store.h`. * * The basic interfaces for store and store iterator implementations are * defined here. New store implementations should include this header and * implement three basic elements: * * - A structure representing the store back end. This structure will be * opaque to all downstream code and its layout is entirely up to the * implementer. * * - A structure representing a store iterator state, also opaque. * * - The LSUP_StoreInt interface with all the functions defined in the * interface necessary to interact with the store. * * See the `store_htable.{c,h}` and `store_mdb.{c,h}` files for examples of * fully functioning implementations. * * The #LSUP_StoreInt structure defines a store interface for raw buffer * triples. Nothing in the store functions' signatures should hint at RDF * triples—they should accept and produce exclusively raw byte buffers * (#LSUP_Buffer). A store interface may have any of the `LSUP_STORE_*` faeture * flags which should be reflected in the way its members are implemented. */ #ifndef _LSUP_STORE_INTERFACE_H #define _LSUP_STORE_INTERFACE_H #include "environment.h" /* * Store feature flags. * * NOTE: LSUP_STORE_PERM need only be set by an implementation based on whether * its path is on a default temporary dir (e.g. LSUP_MDB_RAMDISK_PATH). If this * flag is not set, it means the data will be cleared before the next execution * of the program. However, its being set does not guarantee the persistence of * the medium (i.e. a "permanent" store may have been created ad hoc on a * tempfs). */ typedef enum { LSUP_STORE_PERM = 1<<0, ///< Store is on a permanent support. LSUP_STORE_CTX = 1<<1, ///< Store supports contexts (quads). LSUP_STORE_IDX = 1<<2, ///< Store is fully SPO(C)-indexed. LSUP_STORE_TXN = 1<<3, ///< Supports transaction handling. LSUP_STORE_COW = 1<<4, ///< Copy on write. @sa #iter_next_fn_t() } LSUP_StoreFeature; /* * Store function types. */ /** @brief Prototype: create any environment necessary for the store to work. * * This function should be idempotent on separate calls to the same `id`, * unless the `clear` option is set to `true`. * * @param[in,out] id Identifier to use for the store. This should be * a URI that uniquely identifies a back end for the implementation using it, * e.g. a SQL connection string, file path for an embedded store, the URL of a * REST API endpoint, etc. It may also be NULL, in which case it will be set to * the default identifier set by the implementation. It can be retrieved from * an existing store via #store_id_fn_t . * * @param[in] clear Whether to remove an existing environment with the same ID. */ typedef LSUP_rc (*store_setup_fn_t)(const char *id, bool clear); /** @brief Prototype: create a new store. * * @param[in] id Identifier for the new store. How this is interpreted, and * whether it is even used, depends on the implementation, which should * provide documentation on how to pass and interpret this parameter. * * @param[in] size Initial size for the store. It may be 0. Only meaningful * for stores that may preallocate memory, such as #HTStore. * * @return New store handle. */ typedef void * (*store_new_fn_t)(const char *id, size_t size); /** @brief Prototype: free store handle. * * @param[in] store Store handle. * */ typedef void (*store_free_fn_t)(void *store); /** @brief Prototype: get the store ID. * * @param[in] store Store handle. * * @return store ID string. This is a copy and should be freed after use. */ typedef char * (*store_id_fn_t)(const void *store); /** @brief Prototype: get store size. * * @param[in] store The store to calculate size of. * * @return Number of stored SPO triples (across all contexts if supported). */ typedef size_t (*store_size_fn_t)(const void *store); #if 0 /** @brief Print stats about a store. * * TODO * * @param[in] store The store to get stats for. */ typedef LSUP_rc (*store_stat_fn_t)(void *store, void *stat); #endif /** @brief Begin a transaction. * * Only for LSUP_STORE_TXN stores. * * The transaction handle is managed by the store implementation and can be any * data type. * * @param[in] store Store handle. * * @param[in] flags Transaction flags. These vary with each implementation. * * @param[out] txn Will point to the new open transaction on success, or to * undefined content on failure. * * @return LSUP_OK if the transaction started successfully, <0 on error. */ typedef LSUP_rc (*store_txn_begin_fn_t)(void *store, int flags, void **txn); /** @brief Commit a transaction. * * Only for LSUP_STORE_TXN stores. * * @param[in] store Store handle. * * @param[in] txn Transaction handle generated by #store_txn_begin_fn_t. * * @return LSUP_OK if the transaction was committed successfully, <0 on error. */ typedef LSUP_rc (*store_txn_commit_fn_t)(void *store); /** @brief Abort a transaction. * * Only for LSUP_STORE_TXN stores. * * @param[in] store Store handle. * * @param[in] txn Transaction handle generated by #store_txn_begin_fn_t. */ typedef void (*store_txn_abort_fn_t)(void *store); /** @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 store[in] The store to add to. * * @param sc[in] 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. */ typedef void * (*store_add_init_fn_t)( void *store, const LSUP_Buffer *sc, void *udata); /** @brief Add one triple into the store. * * This must be called after #add_init_fn, using the iterator * yielded by that function. It may be called multiple times and must be * followed by #add_done_fn or #add_abort_fn (if supported). * * @param it[in] Iterator obtained by #store_add_init_fn_t. * The following members are of interest: * it->i stores the total number of records inserted. * * @param sspo[in] 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. */ typedef LSUP_rc (*store_add_iter_fn_t)( void *it, const LSUP_BufferTriple * sspo); /** @brief Abort an add loop and free iterator. * * Usually called on an irrecoverable error from #add_iter_fn. None of the * successful inserts in the same loop is retained. * * @param it[in] Iterator obtained by #store_add_init_fn_t. */ typedef void (*store_add_abort_fn_t)(void *it); /* * Iterator function types. */ /** @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. */ typedef void * (*iter_txn_fn_t)(void *it); /** @brief Finalize an add loop and free iterator. * * This must be called after #add_iter_fn. * * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init. */ typedef LSUP_rc (*store_add_done_fn_t)(void *it); /** @brief Add a single term to the store. * * @param[in] store Store handle. * * @param[in] sterm Serialized term to store. */ typedef LSUP_rc (*store_add_term_fn_t)( void *store, const LSUP_Buffer *sterm, void *udata); /** @brief Prototype: 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[out] ct If not NULL, this will be populated with the number of * entries found. 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. * * @param[in] udata User data. Consult individual store implementations for * how this is interpreted. * * @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 * #LSUP_mdbiter_free() after use. If matches are found, the iterator points to * the first result which can be retrieved with #iter_next_fn(). */ typedef void * (*store_lookup_fn_t)( void *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so, const LSUP_Buffer *sc, void *udata, size_t *ct); /** @brief Prototype: check for existence of a triple (T/F). * * @param[in] Store to be queried. * * @param[in] spo Triple to look up. All members must not be NULL. * * @param[in] c Optional context to look into. It may be NULL. It is * disregarded by stores without the LSUP_STORE_CTX feature. * * @return Whether the triple exist in the store (/context). */ typedef bool (*store_trp_exist_fn_t)( void *store, const LSUP_BufferTriple *sspo, const LSUP_Buffer *sc); /** @brief Prototype: delete triples by pattern matching. * * The ss, sp, so, sc terms act as a matching pattern as documented in * @sa #store_lookup_fn. 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. */ typedef LSUP_rc (*store_remove_fn_t)( void *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so, const LSUP_Buffer *sc, void *udata, size_t *ct); /** @brief Put an in-memory namespace map into a permanent back end. * * This is only available in stores with the LSUP_STORE_PERM feature. * * Existing prefixes and namespaces are not updated. Thus, if the following are * already stored: * * ns1: * ns2: * * Neither of the following will be inserted: * * ns3: * ns2: * * @param[in] store MDB store to update. * * @param[out] nsm Namespace map handle to store. * * @param[in] udata User-defined data. Consult individual implementations for * details. * * @return LSUP_OK if all terms were updated; LSUP_CONFLICT if one or more * namespaces or terms were not updated because they already existed; <0 if * an error occurred. */ typedef LSUP_rc (*store_nsm_put_fn_t)( void *store, const LSUP_NSMap * nsm, void *udata); /** @brief Get the store's namespace prefix map. * * @param[in] store MDB store to query. * * @return NS map or NULL on error. */ typedef LSUP_NSMap * (*store_nsm_get_fn_t)(void *store); /** @brief Prototype: 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. For stores * without the LSUP_STORE_COW, data are copied on retrieval and the resulting * buffers can be freed with #LSUP_buffer_free() or analogous methods. * * @param[in] it Opaque iterator handle obtained with the store's #lookup_fn. * * @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. */ typedef LSUP_rc (*iter_next_fn_t)( void *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx); /** @brief Prototype: free an iterator allocated by a lookup. * * @param it[in] Iterator pointer. It will be set to NULL after freeing. */ typedef void (*iter_free_fn_t)(void * it); /* * Iterface type definitions. */ /** @brief Store interface. * * New store implementations should define a static structure with the relevant * members filled in. Some members are only relevant to certain types of stores * and may be set to NULL. * * #setup_fn may be optionally defined and MUST cause an idempotent action, * unless the `clear` argument is set to `true`. Callers should check if this * member is NULL and if it is not, call it at the beginning of the * interaction with the store. * * Transaction control members are only applicable to stores with the * #LSUP_STORE_TXN feature. */ typedef struct store_if_t { // Basic properties. char name[16]; ///< Store type name. LSUP_StoreFeature features; ///< Feature flags. // Allocation, setup and deallocation. store_setup_fn_t setup_fn; ///< Called before #store_new_fn_t. store_new_fn_t new_fn; ///< Create a new store instance. store_free_fn_t free_fn; ///< Free the store. // Metadata. store_size_fn_t size_fn; ///< Number of triples in the store. store_id_fn_t id_fn; ///< Get store ID. // Transaction control. store_txn_begin_fn_t txn_begin_fn; ///< Begin transaction. store_txn_commit_fn_t txn_commit_fn; ///< Commit transaction. store_txn_abort_fn_t txn_abort_fn; ///< Abort transaction. iter_txn_fn_t iter_txn_fn; ///< Get iterator's transaction. // Addition. store_add_init_fn_t add_init_fn; ///< Initialize add iteration. store_add_iter_fn_t add_iter_fn; ///< Add one triple. store_add_abort_fn_t add_abort_fn; /**< Abort (roll back) the add process. * * Only available in * stores with #LSUP_STORE_TXN * feature. Optional. */ store_add_done_fn_t add_done_fn; ///< Complete the add process. store_add_term_fn_t add_term_fn; /**< Add (index) a term to the store. * * Only available in stores with * #LSUP_STORE_IDX feature. Optional. */ // Look up. store_lookup_fn_t lookup_fn; ///< Look up triples by pattern.  //store_trp_exist_fn_t exist_fn; ///< Check if a triple exists. iter_next_fn_t lu_next_fn; ///< Advance the lookup iterator. iter_free_fn_t lu_free_fn; ///< Free the lookup iterator. // Removal. store_remove_fn_t remove_fn; ///< Remove triples by pattern. // Namespace prefix mapping. store_nsm_put_fn_t nsm_put_fn; /**< Add a ns/pfx pair to the map. * * Only available (and mandatory) * in stores with the * #LSUP_STORE_IDX feature. */ store_nsm_get_fn_t nsm_get_fn; /**< Get a namespace from the map. * * Only available (and mandatory) * in stores with the * #LSUP_STORE_IDX feature. */ } LSUP_StoreInt; /* * Template for a new store and iterator implementation. * These should be placed in the .c file where the interface functions are * defined, and declared as `extern` in the related .h file. const LSUP_StoreInt my_store_int = { .name = "My Store", // Truncated to 15 chars. .features = LSUP_STORE_PERM | LSUP_STORE_IDX, .setup_fn = my_setup_fn, .new_fn = my_new_fn, .free_fn = my_free_fn, .size_fn = my_size_fn, .id_fn = my_id_fn, .txn_begin_fn = my_txn_begin_fn, .txn_commit_fn = my_txn_commit_fn, .txn_abort_fn = my_txn_abort_fn, .add_init_fn = my_init_fn, .add_iter_fn = my_iter_fn, .add_abort_fn = my_add_abort_fn, .add_done_fn = my_add_done_fn, .add_term_fn = my_add_term_fn, .lookup_fn = my_lookup_fn, .lu_next_fn = my_iter_next_fn, .lu_free_fn = my_iter_free_fn, .remove_fn = my_remove_fn, .nsm_put_fn = my_nsm_put_fn, .nsm_get_fn = my_nsm_get_fn, }; */ #endif /* _LSUP_STORE_INTERFACE_H */