/* -*- Mode: c; c-basic-offset: 2 -*- * * rdf_storage_tstore.c - RDF Storage using 3store * * Copyright (C) 2003-2008, David Beckett http://www.dajobe.org/ * Copyright (C) 2003-2004, University of Bristol, UK http://www.bristol.ac.uk/ * * This package is Free Software and part of Redland http://librdf.org/ * * It is licensed under the following three licenses as alternatives: * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version * 2. GNU General Public License (GPL) V2 or any newer version * 3. Apache License, V2.0 or any newer version * * You may not use this file except in compliance with at least one of * the above three licenses. * * See LICENSE.html or LICENSE.txt at the top of this package for the * complete terms and further detail along with the license texts for * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively. * * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 #include #endif #include #include #ifdef HAVE_STDLIB_H #include /* for abort() as used in errors */ #endif #include #include #include typedef struct { /* Tstore args for connecting */ const char *host; const char *db; const char *user; const char *password; const char *model; RDFSQL* rdfsql; } librdf_storage_tstore_context; /* prototypes for local functions */ static int librdf_storage_tstore_init(librdf_storage* storage, const char *name, librdf_hash* options); static int librdf_storage_tstore_open(librdf_storage* storage, librdf_model* model); static int librdf_storage_tstore_close(librdf_storage* storage); static int librdf_storage_tstore_size(librdf_storage* storage); static int librdf_storage_tstore_add_statement(librdf_storage* storage, librdf_statement* statement); static int librdf_storage_tstore_remove_statement(librdf_storage* storage, librdf_statement* statement); static int librdf_storage_tstore_contains_statement(librdf_storage* storage, librdf_statement* statement); static librdf_stream* librdf_storage_tstore_serialise(librdf_storage* storage); static librdf_stream* librdf_storage_tstore_find_statements(librdf_storage* storage, librdf_statement* statement); /* serialising implementing functions */ static int librdf_storage_tstore_serialise_end_of_stream(void* context); static int librdf_storage_tstore_serialise_next_statement(void* context); static void* librdf_storage_tstore_serialise_get_statement(void* context, int flags); static void librdf_storage_tstore_serialise_finished(void* context); /* find implementing functions */ static int librdf_storage_tstore_find_end_of_stream(void* context); static int librdf_storage_tstore_find_next_statement(void* context); static void* librdf_storage_tstore_find_get_statement(void* context, int flags); static void librdf_storage_tstore_find_finished(void* context); /* context functions */ static int librdf_storage_tstore_context_add_statement(librdf_storage* storage, librdf_node* context_node, librdf_statement* statement); static int librdf_storage_tstore_context_remove_statement(librdf_storage* storage, librdf_node* context_node, librdf_statement* statement); static librdf_stream* librdf_storage_tstore_context_serialise(librdf_storage* storage, librdf_node* context_node); /* context list statement stream methods */ #if 0 static int librdf_storage_tstore_context_serialise_end_of_stream(void* context); static int librdf_storage_tstore_context_serialise_next_statement(void* context); static void* librdf_storage_tstore_context_serialise_get_statement(void* context, int flags); static void librdf_storage_tstore_context_serialise_finished(void* context); #endif static librdf_statement* librdf_storage_tstore_statement_from_rs_triple(librdf_world* world, rs_triple *triple); static rs_triple* librdf_storage_tstore_statement_as_rs_triple(librdf_statement *statement); static void librdf_storage_tstore_register_factory(librdf_storage_factory *factory); /* functions implementing storage api */ static int librdf_storage_tstore_init(librdf_storage* storage, const char *name, librdf_hash* options) { librdf_storage_tstore_context *context=(librdf_storage_tstore_context*)storage->context; context->host=librdf_hash_get_del(options, "host"); context->db=librdf_hash_get_del(options, "database"); context->user=librdf_hash_get_del(options, "user"); context->password=librdf_hash_get_del(options, "password"); context->model=librdf_hash_get_del(options, "model"); /* no more options, might as well free them now */ if(options) librdf_free_hash(options); return 0; } static void librdf_storage_tstore_terminate(librdf_storage* storage) { /* nop */ } static int librdf_storage_tstore_open(librdf_storage* storage, librdf_model* model) { librdf_storage_tstore_context *context=(librdf_storage_tstore_context*)storage->context; if(context->host) context->rdfsql=rs_connect_remote(context->host, context->db, context->user, context->password, context->model); else context->rdfsql=rs_connect(context->db, context->user, context->password, context->model); if(!context->rdfsql) return 1; return 0; } /** * librdf_storage_tstore_close: * @storage: the storage * * . * * Close the storage list storage, and free all content since there is no * persistance. * * Return value: non 0 on failure **/ static int librdf_storage_tstore_close(librdf_storage* storage) { /* librdf_storage_tstore_context* context=(librdf_storage_tstore_context*)storage->context; */ return 0; } static int librdf_storage_tstore_size(librdf_storage* storage) { return -1; } static int librdf_storage_tstore_add_statement(librdf_storage* storage, librdf_statement* statement) { /* FIXME - cannot check for adding duplicate statements */ return librdf_storage_tstore_context_add_statement(storage, NULL, statement); } static int librdf_storage_tstore_remove_statement(librdf_storage* storage, librdf_statement* statement) { return librdf_storage_tstore_context_remove_statement(storage, NULL, statement); } static int librdf_storage_tstore_contains_statement(librdf_storage* storage, librdf_statement* statement) { /*librdf_storage_tstore_context* context=(librdf_storage_tstore_context*)storage->context; */ /* FIXME */ return 0; } static librdf_statement* librdf_storage_tstore_statement_from_rs_triple(librdf_world* world, rs_triple *triple) { librdf_node *subject_node; librdf_node *predicate_node; librdf_node *object_node; if(triple->subject) { if(!strncmp(triple->subject, "_:",2)) subject_node=librdf_new_node_from_blank_identifier(world, (const unsigned char *)triple->subject+2); else subject_node=librdf_new_node_from_uri_string(world, (const unsigned char *)triple->subject); if(!subject_node) return NULL; } else subject_node=NULL; if(triple->predicate) { predicate_node=librdf_new_node_from_uri_string(world, (const unsigned char *)triple->predicate); if(!predicate_node) { librdf_free_node(subject_node); return NULL; } } else predicate_node=NULL; if(triple->object) { if(triple->literal) object_node=librdf_new_node_from_typed_literal(world, (const unsigned char *)triple->object, NULL, NULL); else if(!strncmp(triple->object, ":", 2)) object_node=librdf_new_node_from_blank_identifier(world, (const unsigned char *)triple->object+2); else object_node=librdf_new_node_from_uri_string(world, (const unsigned char *)triple->object); if(!object_node) { librdf_free_node(subject_node); librdf_free_node(predicate_node); return NULL; } } else object_node=NULL; return librdf_new_statement_from_nodes(world, subject_node, predicate_node, object_node); } /* FIXME returns an alloced triple pointing to shared strings */ static rs_triple* librdf_storage_tstore_statement_as_rs_triple(librdf_statement *statement) { librdf_node *subject_node=statement->subject; librdf_node *predicate_node=statement->predicate; librdf_node *object_node=statement->object; rs_triple* triple=LIBRDF_MALLOC(rs_triple, sizeof(rs_triple)); if(subject_node) { if(librdf_node_is_blank(subject_node)) triple->subject=(char*)librdf_node_get_blank_identifier(subject_node); else triple->subject=(char*)librdf_uri_as_string(librdf_node_get_uri(subject_node)); } else triple->subject=NULL; if(predicate_node) triple->predicate=(char*)librdf_uri_as_string(librdf_node_get_uri(predicate_node)); else triple->predicate=NULL; /* Assumptions - FIXME */ triple->literal = 0; if(object_node) { if(librdf_node_is_literal(object_node)) { triple->object=(char*)librdf_node_get_literal_value(object_node); triple->literal = 1; } else if(librdf_node_is_blank(object_node)) { triple->object=(char*)librdf_node_get_blank_identifier(object_node); } else { triple->object=(char*)librdf_uri_as_string(librdf_node_get_uri(object_node)); } } else triple->object=NULL; return triple; } typedef struct { librdf_storage* storage; rs_result *result; rs_triple *triple; } librdf_storage_tstore_serialise_stream_context; static librdf_stream* librdf_storage_tstore_serialise(librdf_storage* storage) { librdf_storage_tstore_context* context=(librdf_storage_tstore_context*)storage->context; librdf_storage_tstore_serialise_stream_context* scontext; librdf_stream* stream; scontext=(librdf_storage_tstore_serialise_stream_context*)LIBRDF_CALLOC(librdf_storage_tstore_serialise_stream_context, 1, sizeof(librdf_storage_tstore_serialise_stream_context)); if(!scontext) return NULL; scontext->storage=storage; librdf_storage_add_reference(scontext->storage); scontext->result=rs_find_all_resources(context->rdfsql, 0, context->model); if(!scontext->result) /* empty */ scontext->triple=NULL; else scontext->triple=rs_next_triple(scontext->result); stream=librdf_new_stream(storage->world, (void*)scontext, &librdf_storage_tstore_serialise_end_of_stream, &librdf_storage_tstore_serialise_next_statement, &librdf_storage_tstore_serialise_get_statement, &librdf_storage_tstore_serialise_finished); if(!stream) { librdf_storage_tstore_serialise_finished((void*)scontext); return NULL; } return stream; } static int librdf_storage_tstore_serialise_end_of_stream(void* context) { librdf_storage_tstore_serialise_stream_context* scontext=(librdf_storage_tstore_serialise_stream_context*)context; return scontext->triple == NULL; } static int librdf_storage_tstore_serialise_next_statement(void* context) { librdf_storage_tstore_serialise_stream_context* scontext=(librdf_storage_tstore_serialise_stream_context*)context; if(!scontext->triple) return 1; scontext->triple=rs_next_triple(scontext->result); return scontext->triple == NULL; } static void* librdf_storage_tstore_serialise_get_statement(void* context, int flags) { librdf_storage_tstore_serialise_stream_context* scontext=(librdf_storage_tstore_serialise_stream_context*)context; switch(flags) { case LIBRDF_ITERATOR_GET_METHOD_GET_OBJECT: { librdf_statement* statement=librdf_storage_tstore_statement_from_rs_triple(scontext->storage->world, scontext->triple); return statement; } case LIBRDF_ITERATOR_GET_METHOD_GET_CONTEXT: return NULL; default: librdf_log(scontext->storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Unknown iterator method flag %d", flags); return NULL; } } static void librdf_storage_tstore_serialise_finished(void* context) { librdf_storage_tstore_serialise_stream_context* scontext=(librdf_storage_tstore_serialise_stream_context*)context; if(scontext->triple) { /* The docs say about rs_find_triples:[[ * NB Once rs_find_triples has been called, all the triples * /must/ be fetched with rs_next_triple(), even if they are * not required. * ]] * but let's assume it applies to rs_find_all_resources */ while(rs_next_triple(scontext->result)) ; } if(scontext->result) rs_free_result(scontext->result); if(scontext->storage) librdf_storage_remove_reference(scontext->storage); LIBRDF_FREE(librdf_storage_tstore_serialise_stream_context, scontext); } typedef struct { librdf_storage* storage; rs_result *result; rs_triple *triple; rs_triple *search_triple; } librdf_storage_tstore_find_stream_context; /** * librdf_storage_tstore_find_statements: * @storage: the storage * @statement: the statement to match * * . * * Return a stream of statements matching the given statement (or * all statements if NULL). Parts (subject, predicate, object) of the * statement can be empty in which case any statement part will match that. * Uses #librdf_statement_match to do the matching. * * Return value: a #librdf_stream or NULL on failure **/ static librdf_stream* librdf_storage_tstore_find_statements(librdf_storage* storage, librdf_statement* statement) { librdf_storage_tstore_context* context=(librdf_storage_tstore_context*)storage->context; librdf_storage_tstore_find_stream_context* scontext; librdf_stream* stream; rs_triple* triple; rs_obj_type type; statement=librdf_new_statement_from_statement(statement); if(!statement) return NULL; scontext=(librdf_storage_tstore_find_stream_context*)LIBRDF_CALLOC(librdf_storage_tstore_find_stream_context, 1, sizeof(librdf_storage_tstore_find_stream_context)); if(!scontext) return NULL; scontext->storage=storage; librdf_storage_add_reference(scontext->storage); triple=librdf_storage_tstore_statement_as_rs_triple(statement); scontext->search_triple=triple; if(triple->object) type=(triple->literal ? ObjLiteral: ObjURI); else type=ObjAny; scontext->result=rs_find_triples(context->rdfsql, triple->subject, triple->predicate, triple->object, type, 0, context->model); if(!scontext->result) /* empty */ scontext->triple=NULL; else scontext->triple=rs_next_triple(scontext->result); stream=librdf_new_stream(storage->world, (void*)scontext, &librdf_storage_tstore_find_end_of_stream, &librdf_storage_tstore_find_next_statement, &librdf_storage_tstore_find_get_statement, &librdf_storage_tstore_find_finished); if(!stream) { librdf_storage_tstore_find_finished((void*)scontext); return NULL; } return stream; } static int librdf_storage_tstore_find_end_of_stream(void* context) { librdf_storage_tstore_find_stream_context* scontext=(librdf_storage_tstore_find_stream_context*)context; return scontext->triple == NULL; } static int librdf_storage_tstore_find_next_statement(void* context) { librdf_storage_tstore_find_stream_context* scontext=(librdf_storage_tstore_find_stream_context*)context; if(!scontext->triple) return 1; scontext->triple=rs_next_triple(scontext->result); return scontext->triple == NULL; } static void* librdf_storage_tstore_find_get_statement(void* context, int flags) { librdf_storage_tstore_find_stream_context* scontext=(librdf_storage_tstore_find_stream_context*)context; switch(flags) { case LIBRDF_ITERATOR_GET_METHOD_GET_OBJECT: { librdf_statement* statement=librdf_storage_tstore_statement_from_rs_triple(scontext->storage->world, scontext->triple); return statement; } case LIBRDF_ITERATOR_GET_METHOD_GET_CONTEXT: return NULL; default: librdf_log(scontext->storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Unknown iterator method flag %d", flags); return NULL; } } static void librdf_storage_tstore_find_finished(void* context) { librdf_storage_tstore_find_stream_context* scontext=(librdf_storage_tstore_find_stream_context*)context; if(scontext->triple) { /* The docs say about rs_find_triples:[[ * NB Once rs_find_triples has been called, all the triples * /must/ be fetched with rs_next_triple(), even if they are * not required. * ]] */ while(rs_next_triple(scontext->result)) ; } if(scontext->result) rs_free_result(scontext->result); /* FIXME: as alloced in librdf_storage_tstore_statement_as_rs_triple */ if(scontext->search_triple) LIBRDF_FREE(rs_triple, scontext->search_triple); if(scontext->storage) librdf_storage_remove_reference(scontext->storage); LIBRDF_FREE(librdf_storage_tstore_find_stream_context, scontext); } /** * librdf_storage_tstore_context_add_statement: * @storage: #librdf_storage object * @context_node: #librdf_node object * @statement: #librdf_statement statement to add * * Add a statement to a storage context. * * Return value: non 0 on failure **/ static int librdf_storage_tstore_context_add_statement(librdf_storage* storage, librdf_node* context_node, librdf_statement* statement) { librdf_storage_tstore_context* context=(librdf_storage_tstore_context*)storage->context; librdf_node *subject_node=statement->subject; librdf_node *predicate_node=statement->predicate; librdf_node *object_node=statement->object; char *subject; char *predicate; char *object; rs_obj_type type; if(librdf_node_is_blank(subject_node)) { subject=(char*)librdf_node_get_blank_identifier(subject_node); } else subject=(char*)librdf_uri_as_string(librdf_node_get_uri(subject_node)); predicate=(char*)librdf_uri_as_string(librdf_node_get_uri(predicate_node)); /* Assumptions - FIXME */ if(librdf_node_is_literal(object_node)) { object=(char*)librdf_node_get_literal_value(object_node); type = ObjLiteral; } else if(librdf_node_is_blank(object_node)) { object=(char*)librdf_node_get_blank_identifier(object_node); type = ObjURI; } else { object=(char*)librdf_uri_as_string(librdf_node_get_uri(object_node)); type = ObjURI; } if(rs_assert_triple(context->rdfsql, subject, predicate, object, type)) return 1; return 0; } /** * librdf_storage_tstore_context_remove_statement: * @storage: #librdf_storage object * @context_node: #librdf_node object * @statement: #librdf_statement statement to remove * * Remove a statement from a storage context. * * Return value: non 0 on failure **/ static int librdf_storage_tstore_context_remove_statement(librdf_storage* storage, librdf_node* context_node, librdf_statement* statement) { /* librdf_storage_tstore_context* context=(librdf_storage_tstore_context*)storage->context; */ /* FIXME */ return 0; } typedef struct { librdf_iterator* iterator; librdf_hash_datum *key; librdf_hash_datum *value; librdf_statement current; /* static, shared statement */ } librdf_storage_tstore_context_serialise_stream_context; /** * librdf_storage_tstore_context_serialise: * @storage: #librdf_storage object * @context_node: #librdf_node object * * List all statements in a storage context. * * Return value: #librdf_stream of statements or NULL on failure or context is empty **/ static librdf_stream* librdf_storage_tstore_context_serialise(librdf_storage* storage, librdf_node* context_node) { return NULL; } #if 0 static int librdf_storage_tstore_context_serialise_end_of_stream(void* context) { /* librdf_storage_tstore_context_serialise_stream_context* scontext=(librdf_storage_tstore_context_serialise_stream_context*)context; */ return 1; } static int librdf_storage_tstore_context_serialise_next_statement(void* context) { /* librdf_storage_tstore_context_serialise_stream_context* scontext=(librdf_storage_tstore_context_serialise_stream_context*)context; */ return 1; } static void* librdf_storage_tstore_context_serialise_get_statement(void* context, int flags) { /* librdf_storage_tstore_context_serialise_stream_context* scontext=(librdf_storage_tstore_context_serialise_stream_context*)context; */ return NULL; } static void librdf_storage_tstore_context_serialise_finished(void* context) { librdf_storage_tstore_context_serialise_stream_context* scontext=(librdf_storage_tstore_context_serialise_stream_context*)context; LIBRDF_FREE(librdf_storage_tstore_context_serialise_stream_context, scontext); } #endif /* local function to register list storage functions */ static void librdf_storage_tstore_register_factory(librdf_storage_factory *factory) { factory->context_length = sizeof(librdf_storage_tstore_context); factory->init = librdf_storage_tstore_init; factory->terminate = librdf_storage_tstore_terminate; factory->open = librdf_storage_tstore_open; factory->close = librdf_storage_tstore_close; factory->size = librdf_storage_tstore_size; factory->add_statement = librdf_storage_tstore_add_statement; factory->remove_statement = librdf_storage_tstore_remove_statement; factory->contains_statement = librdf_storage_tstore_contains_statement; factory->serialise = librdf_storage_tstore_serialise; factory->find_statements = librdf_storage_tstore_find_statements; factory->context_add_statement = librdf_storage_tstore_context_add_statement; factory->context_remove_statement = librdf_storage_tstore_context_remove_statement; factory->context_serialise = librdf_storage_tstore_context_serialise; } /** * librdf_init_storage_tstore: * @world: world object * * INTERNAL - initialise the storage_tstore module. **/ void librdf_init_storage_tstore(librdf_world *world) { librdf_storage_register_factory(world, "tstore", "AKT triplestore", &librdf_storage_tstore_register_factory); }