#include "graph.h" /* * Data types. */ struct graph_t { LSUP_Term *uri; ///< Graph "name" (URI). LSUP_Store * store; ///< Store handle. LSUP_NSMap * nsm; /**< Namespace map. NOTE: This is * NULL for permanent stores. */ }; struct graph_iter_t { LSUP_Store * store; ///< Store tied to the iterator. void * data; ///< Iterator state. size_t ct; ///< Total lookup matches. }; /* * Static prototypes. */ inline static LSUP_rc graph_iter_next_buffer (LSUP_GraphIterator *it, LSUP_BufferTriple *sspo); #define ENTRY(a, b) (be) == (LSUP_STORE_##a) || static inline bool check_backend (LSUP_StoreType be) { return (BACKEND_TBL false); } #undef ENTRY /* * Graph API. */ LSUP_Graph * LSUP_graph_new ( LSUP_Term *uri, const LSUP_StoreType store_type, const char *store_id, LSUP_NSMap *nsm, size_t size) { if (UNLIKELY (!LSUP_IS_INIT)) { log_error ( "Environment is not initialized. Did you call LSUP_init()?"); return NULL; } if (UNLIKELY (!check_backend (store_type))) return NULL; LSUP_Graph *gr; MALLOC_GUARD (gr, NULL); gr->uri = uri; MALLOC_GUARD (gr->store, NULL); gr->store->type = store_type; gr->store->id = store_id ? strdup (store_id) : NULL; switch (gr->store->type) { #define ENTRY(a, b) \ case LSUP_STORE_##a: gr->store->sif = &b; break; BACKEND_TBL #undef ENTRY default: log_error ("Not a valid store type: %d", store_type); return NULL; }; // TODO implement custom default context. gr->store->data = gr->store->sif->new_fn (store_id, size); if (gr->store->sif->features & LSUP_STORE_PERM) gr->nsm = NULL; else gr->nsm = nsm ? nsm : LSUP_default_nsm; log_debug ("Graph created."); return gr; } LSUP_rc LSUP_graph_bool_op( const LSUP_bool_op op, const LSUP_Graph *gr1, const LSUP_Graph *gr2, LSUP_Graph *res) { LSUP_rc rc = LSUP_NOACTION; if (UNLIKELY ( op != LSUP_BOOL_UNION && op != LSUP_BOOL_SUBTRACTION && op != LSUP_BOOL_INTERSECTION && op != LSUP_BOOL_XOR)) { log_error ("Invalid boolean operation: %d.", op); return LSUP_VALUE_ERR; } if (op == LSUP_BOOL_UNION) { rc = LSUP_graph_copy_contents (gr1, res); PCHECK (rc, fail); rc = LSUP_graph_copy_contents (gr2, res); PCHECK (rc, fail); return LSUP_OK; } LSUP_Buffer *res_sc = LSUP_term_serialize (res->uri), *gr1_sc = LSUP_term_serialize (gr1->uri), *gr2_sc = LSUP_term_serialize (gr2->uri); void *lu1_it, *lu2_it, *add_it; LSUP_BufferTriple *sspo = BTRP_DUMMY; size_t ct; add_it = res->store->sif->add_init_fn (res->store->data, res_sc); if (op == LSUP_BOOL_XOR) { // Add triples from gr2 if not found in gr1. lu2_it = gr2->store->sif->lookup_fn ( gr2->store->data, NULL, NULL, NULL, gr2_sc, NULL); while (gr2->store->sif->lu_next_fn (lu2_it, sspo, NULL) == LSUP_OK) { lu1_it = gr1->store->sif->lookup_fn ( gr1->store->data, sspo->s, sspo->p, sspo->o, gr1_sc, &ct); if (ct > 0) res->store->sif->add_iter_fn (add_it, sspo); gr1->store->sif->lu_free_fn (lu1_it); } gr2->store->sif->lu_free_fn (lu2_it); } lu1_it = gr1->store->sif->lookup_fn ( gr1->store->data, NULL, NULL, NULL, gr1_sc, NULL); while (gr1->store->sif->lu_next_fn (lu1_it, sspo, NULL) == LSUP_OK) { lu2_it = gr2->store->sif->lookup_fn ( gr2->store->data, sspo->s, sspo->p, sspo->o, gr2_sc, &ct); // For XOR and subtraction, add if not found. // For intersection, add if found. if ((ct == 0) ^ (op == LSUP_BOOL_INTERSECTION)) res->store->sif->add_iter_fn (add_it, sspo); gr2->store->sif->lu_free_fn (lu2_it); } gr1->store->sif->lu_free_fn (lu1_it); res->store->sif->add_done_fn (add_it); LSUP_buffer_free (res_sc); LSUP_buffer_free (gr1_sc); LSUP_buffer_free (gr2_sc); return rc; fail: LSUP_graph_free (res); return rc; } void LSUP_graph_free (LSUP_Graph *gr) { if (UNLIKELY (!gr)) return; LSUP_term_free (gr->uri); free (gr->store->id); gr->store->sif->free_fn (gr->store->data); free (gr->store); free (gr); } LSUP_Term * LSUP_graph_uri (const LSUP_Graph *gr) { return gr->uri; } LSUP_rc LSUP_graph_set_uri (LSUP_Graph *gr, LSUP_Term *uri) { if (!LSUP_IS_IRI (uri)) { log_error ("Term provided is not a IRI."); return LSUP_VALUE_ERR; } LSUP_term_free (gr->uri); gr->uri = uri; return LSUP_OK; } LSUP_NSMap * LSUP_graph_namespace (const LSUP_Graph *gr) { // If nsm_get_fn is not defined, the store has no own NS map. if (!gr->store->sif->nsm_get_fn) return gr->nsm; return gr->store->sif->nsm_get_fn (gr->store->data); } void LSUP_graph_set_namespace (LSUP_Graph *gr, LSUP_NSMap *nsm) { if (!gr->store->sif->nsm_get_fn) gr->nsm = nsm; else log_warn ("Graph back end has a stored NS map."); } size_t LSUP_graph_size (const LSUP_Graph *gr) { return gr->store->sif->size_fn (gr->store->data); } bool LSUP_graph_equals (const LSUP_Graph *gr1, const LSUP_Graph *gr2) { LSUP_Graph *res = LSUP_graph_new (NULL, LSUP_STORE_HTABLE, NULL, NULL, 0); LSUP_graph_bool_op (LSUP_BOOL_XOR, gr1, gr2, res); bool ret = (LSUP_graph_size (res) == 0); LSUP_graph_free (res); return ret; } LSUP_GraphIterator * LSUP_graph_add_init (LSUP_Graph *gr) { LSUP_GraphIterator *it; CALLOC_GUARD (it, NULL); LSUP_Buffer *sc = LSUP_term_serialize (gr->uri); it->data = gr->store->sif->add_init_fn (gr->store->data, sc); LSUP_buffer_free (sc); it->store = gr->store; return it; } LSUP_rc LSUP_graph_add_iter (LSUP_GraphIterator *it, const LSUP_Triple *spo) { LSUP_BufferTriple *sspo = LSUP_triple_serialize (spo); if (UNLIKELY (!sspo)) return LSUP_MEM_ERR; LSUP_rc rc = it->store->sif->add_iter_fn (it->data, sspo); PCHECK (rc, finally); // Store datatype term permanently if the store supports it. if (rc == LSUP_OK && it->store->sif->add_term_fn) { for (int i = 0; i < 3; i++) { LSUP_Term *term = LSUP_triple_pos (spo, i); if (term->type == LSUP_TERM_LITERAL) { LSUP_Buffer *ser_dtype = LSUP_term_serialize (term->datatype); it->store->sif->add_term_fn (it->store->data, ser_dtype); LSUP_buffer_free (ser_dtype); } } } finally: LSUP_btriple_free (sspo); return rc; } void LSUP_graph_add_done (LSUP_GraphIterator *it) { it->store->sif->add_done_fn (it->data); free (it); } LSUP_rc LSUP_graph_add (LSUP_Graph *gr, const LSUP_Triple trp[], size_t *ct) { LSUP_rc rc = LSUP_NOACTION; // Initialize iterator. LSUP_GraphIterator *it = LSUP_graph_add_init (gr); if (ct) *ct = 0; // Serialize and insert RDF triples. for (size_t i = 0; trp[i].s != NULL; i++) { log_trace ("Inserting triple #%lu", i); LSUP_rc db_rc = LSUP_graph_add_iter (it, trp + i); if (db_rc == LSUP_OK) { rc = LSUP_OK; if (ct) (*ct)++; // A duplicate will return LSUP_NOACTION and not increment the // counter. } if (UNLIKELY (db_rc < 0)) { rc = db_rc; goto finally; } } finally: LSUP_graph_add_done (it); return rc; } LSUP_rc LSUP_graph_remove ( LSUP_Graph *gr, const LSUP_Term *s, const LSUP_Term *p, const LSUP_Term *o, size_t *ct) { LSUP_rc rc; LSUP_Buffer *ss = LSUP_term_serialize (s), *sp = LSUP_term_serialize (p), *so = LSUP_term_serialize (o), *sc = LSUP_term_serialize (gr->uri); rc = gr->store->sif->remove_fn (gr->store->data, ss, sp, so, sc, ct); LSUP_buffer_free (ss); LSUP_buffer_free (sp); LSUP_buffer_free (so); LSUP_buffer_free (sc); return rc; } /** * Copy triples from a source graph into a destination one. * * The destination graph is not initialized here, so the copy is cumulative. */ LSUP_rc LSUP_graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest) { LSUP_rc rc = LSUP_NOACTION; LSUP_GraphIterator *it = LSUP_graph_lookup (src, NULL, NULL, NULL, NULL); LSUP_Triple spo; LSUP_GraphIterator *add_it = LSUP_graph_add_init (dest); while (LSUP_graph_iter_next (it, &spo) != LSUP_END) { LSUP_rc add_rc = LSUP_graph_add_iter (add_it, &spo); LSUP_triple_done (&spo); if (LIKELY (add_rc == LSUP_OK)) rc = LSUP_OK; else if (add_rc < 0) { rc = add_rc; break; } } LSUP_graph_add_done (add_it); LSUP_graph_iter_free (it); return rc; } LSUP_GraphIterator * LSUP_graph_lookup ( const LSUP_Graph *gr, const LSUP_Term *s, const LSUP_Term *p, const LSUP_Term *o, size_t *ct) { LSUP_GraphIterator *it; MALLOC_GUARD (it, NULL); it->store = gr->store; LSUP_Buffer *ss = LSUP_term_serialize (s), *sp = LSUP_term_serialize (p), *so = LSUP_term_serialize (o), *sc = LSUP_term_serialize (gr->uri); it->data = it->store->sif->lookup_fn (it->store->data, ss, sp, so, sc, ct); if (UNLIKELY (!it->data)) { free (it); it = NULL; } LSUP_buffer_free (ss); LSUP_buffer_free (sp); LSUP_buffer_free (so); LSUP_buffer_free (sc); return it; } LSUP_rc LSUP_graph_iter_next (LSUP_GraphIterator *it, LSUP_Triple *spo) { LSUP_Buffer *ss, *sp, *so; LSUP_BufferTriple *sspo; if (it->store->sif->features & LSUP_STORE_COW) { CALLOC_GUARD (ss, LSUP_MEM_ERR); CALLOC_GUARD (sp, LSUP_MEM_ERR); CALLOC_GUARD (so, LSUP_MEM_ERR); sspo = LSUP_btriple_new (ss, sp, so); } else { // TODO copy-on-retrieval stores. None yet. } LSUP_rc rc = graph_iter_next_buffer (it, sspo); if (rc == LSUP_OK) { spo->s = LSUP_term_new_from_buffer (sspo->s); if (!spo->s) return LSUP_ERROR; spo->p = LSUP_term_new_from_buffer (sspo->p); if (!spo->p) return LSUP_ERROR; spo->o = LSUP_term_new_from_buffer (sspo->o); if (!spo->o) return LSUP_ERROR; } if (it->store->sif->features & LSUP_STORE_COW) { LSUP_btriple_free_shallow (sspo); } else { // TODO copy-on-retrieval stores. None yet. } return rc; } void LSUP_graph_iter_free (LSUP_GraphIterator *it) { it->store->sif->lu_free_fn (it->data); free (it); } bool LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo) { LSUP_GraphIterator *it = LSUP_graph_lookup ( gr, spo->s, spo->p, spo->o, NULL); LSUP_Triple *tmp_spo = TRP_DUMMY; bool rc = LSUP_graph_iter_next (it, tmp_spo) != LSUP_END; LSUP_triple_free (tmp_spo); LSUP_graph_iter_free (it); return rc; } /* * Static functions. */ /** @brief Advance an iterator and return a serialized triple. * * This is an internal function to pass raw buffers between higher-level * functions without serializing and deserializing triples. */ inline static LSUP_rc graph_iter_next_buffer (LSUP_GraphIterator *it, LSUP_BufferTriple *sspo) { return it->store->sif->lu_next_fn (it->data, sspo, NULL); } /** * Extern inline definitions. */ size_t LSUP_graph_size (const LSUP_Graph *gr);