/* -*- Mode: c; c-basic-offset: 2 -*- * * rdf_storage.c - RDF Storage (Triple store) interface * * Copyright (C) 2000-2008, David Beckett http://www.dajobe.org/ * Copyright (C) 2000-2005, 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 #include #include #ifdef HAVE_STDLIB_H #include /* for abort() as used in errors */ #endif #ifdef MODULAR_LIBRDF #include #endif #include #include #ifndef STANDALONE /* prototypes for functions implementing get_sources, arcs, targets * librdf_iterator via conversion from a librdf_stream of librdf_statement */ static int librdf_storage_stream_to_node_iterator_is_end(void* iterator); static int librdf_storage_stream_to_node_iterator_next_method(void* iterator); static void* librdf_storage_stream_to_node_iterator_get_method(void* iterator, int flags); static void librdf_storage_stream_to_node_iterator_finished(void* iterator); /* helper function for creating iterators for get sources, targets, arcs */ static librdf_iterator* librdf_storage_node_stream_to_node_create(librdf_storage* storage, librdf_node* node1, librdf_node *node2, librdf_statement_part want); #ifdef MODULAR_LIBRDF /* helper function for dynamically loading storage modules */ static lt_dlhandle librdf_storage_load_module(librdf_world *world, const char* lib_name, const char* init_func_name); #endif /** * librdf_init_storage: * @world: redland world object * * INTERNAL - Initialise the storage module. * * Initialises and registers all * compiled storage modules. Must be called before using any of the storage * factory functions such as librdf_get_storage_factory() **/ void librdf_init_storage(librdf_world *world) { #ifdef MODULAR_LIBRDF lt_dlhandle module = NULL; if (!world->storage_modules) world->storage_modules = raptor_new_sequence( (raptor_sequence_free_handler *)lt_dlclose, NULL); #endif /* Always have storage memory - must always be the default storage */ librdf_init_storage_list(world); /* Always have storage list, hashes, file implementations available */ librdf_init_storage_hashes(world); #ifdef STORAGE_TREES librdf_init_storage_trees(world); #endif #ifdef MODULAR_LIBRDF #ifdef STORAGE_FILE module = librdf_storage_load_module(world, "librdf_storage_file", "librdf_init_storage_file"); if (module) raptor_sequence_push(world->storage_modules, module); #endif #ifdef STORAGE_MYSQL module = librdf_storage_load_module(world, "librdf_storage_mysql", "librdf_init_storage_mysql"); if (module) raptor_sequence_push(world->storage_modules, module); #endif #ifdef STORAGE_POSTGRESQL module = librdf_storage_load_module(world, "librdf_storage_postgresql", "librdf_init_storage_postgresql"); if (module) raptor_sequence_push(world->storage_modules, module); #endif #ifdef STORAGE_TSTORE module = librdf_storage_load_module(world, "librdf_storage_tstore", "librdf_init_storage_tstore"); if (module) raptor_sequence_push(world->storage_modules, module); #endif #ifdef STORAGE_SQLITE module = librdf_storage_load_module(world, "librdf_storage_sqlite", "librdf_init_storage_sqlite"); if (module) raptor_sequence_push(world->storage_modules, module); #endif #else /* if !MODULAR_LIBRDF */ #ifdef STORAGE_FILE librdf_init_storage_file(world); #endif #ifdef STORAGE_MYSQL librdf_init_storage_mysql(world); #endif #ifdef STORAGE_POSTGRESQL librdf_init_storage_postgresql(world); #endif #ifdef STORAGE_TSTORE librdf_init_storage_tstore(world); #endif #ifdef STORAGE_SQLITE librdf_init_storage_sqlite(world); #endif #endif } /** * librdf_finish_storage: * @world: redland world object * * INTERNAL - Terminate the storage module. * **/ void librdf_finish_storage(librdf_world *world) { #ifdef MODULAR_LIBRDF if(world->storage_modules) { raptor_free_sequence(world->storage_modules); world->storage_modules=NULL; } #endif if(world->storages) { raptor_free_sequence(world->storages); world->storages=NULL; } } /* helper functions */ static void librdf_free_storage_factory(librdf_storage_factory* factory) { if(factory->name) LIBRDF_FREE(librdf_storage_factory, factory->name); if(factory->label) LIBRDF_FREE(librdf_storage_factory, factory->label); LIBRDF_FREE(librdf_storage_factory, factory); } #ifdef MODULAR_LIBRDF /** * librdf_storage_load_module: * @world: redland world object * @lib_name: base name of shared library file * @init_func_name: name of initialization function in library * * INTERNAL - Load and initialize/register a storage module **/ static lt_dlhandle librdf_storage_load_module(librdf_world *world, const char* lib_name, const char* init_func_name) { typedef void init_func_t(librdf_world* world); init_func_t* init; lt_dlhandle module = lt_dlopenext(lib_name); if (module) { init = (init_func_t*)lt_dlsym(module, init_func_name); if (init) { init(world); } else { LIBRDF_DEBUG2("Failed to initialize storage module %s\n", lib_name); lt_dlclose(module); module = NULL; } } else { LIBRDF_DEBUG2("Failed to load storage module %s\n", lib_name); } return module; } #endif /* class methods */ /** * librdf_storage_register_factory: * @world: redland world object * @name: the storage factory name * @label: the storage factory label * @factory: pointer to function to call to register the factory * * Register a storage factory. **/ void librdf_storage_register_factory(librdf_world* world, const char *name, const char *label, void (*factory) (librdf_storage_factory*)) { librdf_storage_factory *storage; int i; librdf_world_open(world); #if defined(LIBRDF_DEBUG) && LIBRDF_DEBUG > 1 LIBRDF_DEBUG2("Received registration for storage %s\n", name); #endif if(!world->storages) { world->storages=raptor_new_sequence((raptor_sequence_free_handler *)librdf_free_storage_factory, NULL); if(!world->storages) goto oom; } for(i=0; (storage=(librdf_storage_factory*)raptor_sequence_get_at(world->storages, i)); i++) { if(!strcmp(storage->name, name)) { librdf_log(world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "storage %s already registered", storage->name); return; } } storage=(librdf_storage_factory*)LIBRDF_CALLOC(librdf_storage_factory, 1, sizeof(librdf_storage_factory)); if(!storage) goto oom; storage->name=(char*)LIBRDF_MALLOC(cstring, strlen(name)+1); if(!storage->name) goto oom_tidy; strcpy(storage->name, name); storage->label=(char*)LIBRDF_MALLOC(cstring, strlen(label)+1); if(!storage->label) goto oom_tidy; strcpy(storage->label, label); if(raptor_sequence_push(world->storages, storage)) goto oom; /* Call the storage registration function on the new object */ (*factory)(storage); #if defined(LIBRDF_DEBUG) && LIBRDF_DEBUG > 1 LIBRDF_DEBUG3("%s has context size %d\n", name, storage->context_length); #endif return; oom_tidy: librdf_free_storage_factory(storage); oom: LIBRDF_FATAL1(world, LIBRDF_FROM_STORAGE, "Out of memory"); } /** * librdf_get_storage_factory: * @world: redland world object * @name: the factory name or NULL for the default factory * * Get a storage factory by name. * * Return value: the factory object or NULL if there is no such factory **/ librdf_storage_factory* librdf_get_storage_factory(librdf_world* world, const char *name) { librdf_storage_factory *factory; librdf_world_open(world); /* return 1st storage if no particular one wanted - why? */ if(!name) { factory=(librdf_storage_factory *)raptor_sequence_get_at(world->storages, 0); if(!factory) { LIBRDF_DEBUG1("No (default) storages registered\n"); return NULL; } } else { int i; for(i=0; (factory=(librdf_storage_factory*)raptor_sequence_get_at(world->storages, i)); i++) { if(!strcmp(factory->name, name)) break; } /* else FACTORY name not found */ if(!factory) { LIBRDF_DEBUG2("No storage with name %s found\n", name); return NULL; } } return factory; } /** * librdf_storage_enumerate: * @world: redland world object * @counter: index into the list of storages * @name: pointer to store the name of the storage (or NULL) * @label: pointer to store syntax readable label (or NULL) * * Get information on storages. * * Return value: non 0 on failure of if counter is out of range **/ int librdf_storage_enumerate(librdf_world* world, const unsigned int counter, const char **name, const char **label) { librdf_storage_factory *factory; librdf_world_open(world); factory=(librdf_storage_factory*)raptor_sequence_get_at(world->storages, counter); if(!factory) return 1; if(name) *name=factory->name; if(label) *label=factory->label; return 0; } /** * librdf_new_storage: * @world: redland world object * @storage_name: the storage factory name * @name: an identifier for the storage * @options_string: options to initialise storage * * Constructor - create a new #librdf_storage object. * * The options are encoded as described in librdf_hash_from_string() * and can be NULL if none are required. * * Return value: a new #librdf_storage object or NULL on failure * */ librdf_storage* librdf_new_storage(librdf_world *world, const char *storage_name, const char *name, const char *options_string) { librdf_storage_factory* factory; librdf_hash* options_hash; librdf_world_open(world); factory=librdf_get_storage_factory(world, storage_name); if(!factory) return NULL; options_hash=librdf_new_hash(world, NULL); if(!options_hash) return NULL; if(librdf_hash_open(options_hash, NULL, 0, 1, 1, NULL)) { librdf_free_hash(options_hash); return NULL; } if(librdf_hash_from_string(options_hash, options_string)) { librdf_free_hash(options_hash); return NULL; } return librdf_new_storage_from_factory(world, factory, name, options_hash); } /** * librdf_new_storage_with_options: * @world: redland world object * @storage_name: the storage factory name * @name: an identifier for the storage * @options: #librdf_hash of options to use * * Constructor - create a new #librdf_storage object. * * The options can be NULL if none are required. * * Return value: a new #librdf_storage object or NULL on failure * */ librdf_storage* librdf_new_storage_with_options(librdf_world *world, const char *storage_name, const char *name, librdf_hash *options) { librdf_storage_factory* factory; librdf_hash* options_hash; librdf_world_open(world); factory=librdf_get_storage_factory(world, storage_name); if(!factory) return NULL; options_hash=librdf_new_hash_from_hash(options); if(!options_hash) return NULL; if(librdf_hash_open(options_hash, NULL, 0, 1, 1, NULL)) { librdf_free_hash(options_hash); return NULL; } return librdf_new_storage_from_factory(world, factory, name, options_hash); } /** * librdf_new_storage_from_storage - Copy constructor - create a new librdf_storage object from an existing one * @old_storage: the existing storage #librdf_storage to use * * Should create a new storage in the same context as the existing one * as appropriate for the storage. For example, in a RDBMS storage * it would be a new database, or in on disk it would be a new * set of files. This will mean automatically generating * a new identifier for the storage, maybe based on the existing * storage identifier. * * Return value: a new #librdf_storage object or NULL on failure * */ librdf_storage* librdf_new_storage_from_storage(librdf_storage* old_storage) { librdf_storage* new_storage; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(old_storage, librdf_storage, NULL); if(!old_storage->factory->clone) { librdf_log(old_storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "clone method not implemented for storage factory %s", old_storage->factory->name); return NULL; } new_storage=(librdf_storage*)LIBRDF_CALLOC(librdf_storage, 1, sizeof(librdf_storage)); if(!new_storage) return NULL; /* set usage to 1 early to allow cleanup with librdf_free_storage() */ new_storage->usage=1; new_storage->context=(char*)LIBRDF_CALLOC(librdf_storage_context, 1, old_storage->factory->context_length); if(!new_storage->context) { librdf_free_storage(new_storage); return NULL; } new_storage->world=old_storage->world; /* do this now so librdf_free_storage won't call new factory on * partially copied storage */ new_storage->factory=old_storage->factory; /* clone is assumed to do leave the new storage in the same state * after an init() method on an existing storage - i.e ready to * use but closed. */ if(old_storage->factory->clone(new_storage, old_storage)) { librdf_free_storage(new_storage); return NULL; } return new_storage; } /** * librdf_new_storage_from_factory: * @world: redland world object * @factory: the factory to use to construct the storage * @name: name to use for storage * @options: #librdf_hash of options to initialise storage * * Constructor - create a new #librdf_storage object. * * If the options are present, they become owned by the storage * and should no longer be used. * * Return value: a new #librdf_storage object or NULL on failure * */ librdf_storage* librdf_new_storage_from_factory(librdf_world *world, librdf_storage_factory* factory, const char *name, librdf_hash* options) { librdf_storage* storage; librdf_world_open(world); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(factory, librdf_storage_factory, NULL); if(!factory) { librdf_free_hash(options); return NULL; } storage=(librdf_storage*)LIBRDF_CALLOC(librdf_storage, 1, sizeof(librdf_storage)); if(!storage) { librdf_free_hash(options); return NULL; } storage->world=world; /* set usage to 1 early to allow cleanup with librdf_free_storage() */ storage->usage=1; storage->context=(char*)LIBRDF_CALLOC(librdf_storage_context, 1, factory->context_length); if(!storage->context) { librdf_free_hash(options); librdf_free_storage(storage); return NULL; } storage->factory=factory; if(factory->init(storage, name, options)) { librdf_free_storage(storage); return NULL; } return storage; } /** * librdf_free_storage: * @storage: #librdf_storage object * * Destructor - destroy a #librdf_storage object. **/ void librdf_free_storage(librdf_storage* storage) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN(storage, librdf_storage); if(--storage->usage) return; if(storage->factory) storage->factory->terminate(storage); if(storage->context) LIBRDF_FREE(librdf_storage_context, storage->context); LIBRDF_FREE(librdf_storage, storage); } /** * librdf_storage_add_reference: * @storage: #librdf_storage object * * Increment storage reference count by one. * This function is intended to be internal to librdf storage modules. **/ void librdf_storage_add_reference(librdf_storage *storage) { storage->usage++; } /** * librdf_storage_remove_reference(libdf_storage *storage) * @storage: #librdf_storage object * * Decrement storage reference count by one. * Free the storage if reference count becomes zero. * This function is intended to be internal to librdf storage modules. **/ void librdf_storage_remove_reference(librdf_storage *storage) { librdf_free_storage(storage); } /* methods */ /** * librdf_storage_open: * @storage: #librdf_storage object * @model: model stored * * Start a model / storage association. * * This is ended with librdf_storage_close() * * Return value: non 0 on failure **/ int librdf_storage_open(librdf_storage* storage, librdf_model* model) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); return storage->factory->open(storage, model); } /** * librdf_storage_close: * @storage: #librdf_storage object * * End a model / storage association. * * Return value: non 0 on failure **/ int librdf_storage_close(librdf_storage* storage) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); return storage->factory->close(storage); } /** * librdf_storage_size: * @storage: #librdf_storage object * * Get the number of statements stored. * * Return value: The number of statements or < 0 if cannot be determined **/ int librdf_storage_size(librdf_storage* storage) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, -1); return storage->factory->size(storage); } /** * librdf_storage_add_statement: * @storage: #librdf_storage object * @statement: #librdf_statement statement to add * * Add a statement to a storage. * * The passed-in statement is copied when added to the store, not * shared with the store. * * If the statement already exists in the store, it is not added * unless Redland contexts are being used. * * Enforces that the statement is legal for RDF - URI or blank subject, * URI predicate and URI or blank or literal object (i.e. anything). * * Return value: non 0 on failure, <0 on error, >0 if statement was illegal **/ int librdf_storage_add_statement(librdf_storage* storage, librdf_statement* statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 1); /* subject can be a URI or blank node */ if(!librdf_node_is_resource(statement->subject) && !librdf_node_is_blank(statement->subject)) return 1; /* predicate can only be a URI */ if(!librdf_node_is_resource(statement->predicate)) return 1; /* object can be any node - no check needed */ if(storage->factory->add_statement) return storage->factory->add_statement(storage, statement); return -1; } /** * librdf_storage_add_statements: * @storage: #librdf_storage object * @statement_stream: #librdf_stream of statements * * Add a stream of statements to the storage. * * If any of the statements already exists in the store, they are not * added unless Redland contexts are being used. * * Return value: non 0 on failure **/ int librdf_storage_add_statements(librdf_storage* storage, librdf_stream* statement_stream) { int status=0; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement_stream, librdf_stream, 1); if(storage->factory->add_statements) return storage->factory->add_statements(storage, statement_stream); while(!librdf_stream_end(statement_stream)) { librdf_statement* statement=librdf_stream_get_object(statement_stream); if(statement) { status=librdf_storage_add_statement(storage, statement); if(status > 0) /* just skip illegal statements */ status=0; } else status=1; if(status) break; librdf_stream_next(statement_stream); } return status; } /** * librdf_storage_remove_statement: * @storage: #librdf_storage object * @statement: #librdf_statement statement to remove * * Remove a statement from the storage. * * Return value: non 0 on failure **/ int librdf_storage_remove_statement(librdf_storage* storage, librdf_statement* statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 1); if(storage->factory->remove_statement) return storage->factory->remove_statement(storage, statement); return 1; } /** * librdf_storage_contains_statement: * @storage: #librdf_storage object * @statement: #librdf_statement statement to check * * Test if a given statement is present in the storage. * * Return value: non 0 if the storage contains the statement (>0 if illegal statement) **/ int librdf_storage_contains_statement(librdf_storage* storage, librdf_statement* statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 1); /* subject can be a URI or blank node */ if(!statement->subject || (!librdf_node_is_resource(statement->subject) && !librdf_node_is_blank(statement->subject))) return 1; /* predicate can only be a URI */ if(!statement->predicate || !librdf_node_is_resource(statement->predicate)) return 1; if(!statement->object) return 1; return storage->factory->contains_statement(storage, statement); } /** * librdf_storage_serialise: * @storage: #librdf_storage object * * Serialise the storage as a librdf_stream of statemetns. * * Return value: #librdf_stream of statements or NULL on failure **/ librdf_stream* librdf_storage_serialise(librdf_storage* storage) { return storage->factory->serialise(storage); } /** * librdf_storage_find_statements: * @storage: #librdf_storage object * @statement: #librdf_statement partial statement to find * * Search the storage for matching statements. * * Searches the storage for a (partial) statement as described in * librdf_statement_match() and returns a #librdf_stream of * matching #librdf_statement objects. * * Return value: #librdf_stream of matching statements (may be empty) or NULL on failure **/ librdf_stream* librdf_storage_find_statements(librdf_storage* storage, librdf_statement* statement) { librdf_node *subject, *predicate, *object; librdf_iterator *iterator; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); subject=librdf_statement_get_subject(statement); predicate=librdf_statement_get_predicate(statement); object=librdf_statement_get_object(statement); /* try to pick the most efficient storage back end */ /* only subject/source field blank -> use find_sources */ if(storage->factory->find_sources && !subject && predicate && object) { iterator=storage->factory->find_sources(storage, predicate, object); if(iterator) return librdf_new_stream_from_node_iterator(iterator, statement, LIBRDF_STATEMENT_SUBJECT); return NULL; } /* only predicate/arc field blank -> use find_arcs */ if(storage->factory->find_arcs && subject && !predicate && object) { iterator=storage->factory->find_arcs(storage, subject, object); if(iterator) return librdf_new_stream_from_node_iterator(iterator, statement, LIBRDF_STATEMENT_PREDICATE); return NULL; } /* only object/target field blank -> use find_targets */ if(storage->factory->find_targets && subject && predicate && !object) { iterator=storage->factory->find_targets(storage, subject, predicate); if(iterator) return librdf_new_stream_from_node_iterator(iterator, statement, LIBRDF_STATEMENT_OBJECT); return NULL; } return storage->factory->find_statements(storage, statement); } typedef struct { librdf_storage *storage; librdf_stream *stream; librdf_statement *partial_statement; librdf_statement_part want; librdf_node *object_node; librdf_node *context_node; } librdf_storage_stream_to_node_iterator_context; static int librdf_storage_stream_to_node_iterator_is_end(void* iterator) { librdf_storage_stream_to_node_iterator_context* context=(librdf_storage_stream_to_node_iterator_context*)iterator; return librdf_stream_end(context->stream); } static int librdf_storage_stream_to_node_iterator_next_method(void* iterator) { librdf_storage_stream_to_node_iterator_context* context=(librdf_storage_stream_to_node_iterator_context*)iterator; if(context->object_node) { librdf_free_node(context->object_node); context->object_node=NULL; } if(context->context_node) { librdf_free_node(context->context_node); context->context_node=NULL; } return librdf_stream_next(context->stream); } static void* librdf_storage_stream_to_node_iterator_get_method(void* iterator, int flags) { librdf_storage_stream_to_node_iterator_context* context=(librdf_storage_stream_to_node_iterator_context*)iterator; librdf_node* node; librdf_statement* statement=librdf_stream_get_object(context->stream); if(!statement) return NULL; switch(flags) { case LIBRDF_ITERATOR_GET_METHOD_GET_OBJECT: if(!context->object_node) { switch(context->want) { case LIBRDF_STATEMENT_SUBJECT: /* SOURCES (subjects) */ node=librdf_statement_get_subject(statement); break; case LIBRDF_STATEMENT_PREDICATE: /* ARCS (predicates) */ node=librdf_statement_get_predicate(statement); break; case LIBRDF_STATEMENT_OBJECT: /* TARGETS (objects) */ node=librdf_statement_get_object(statement); break; case LIBRDF_STATEMENT_ALL: default: /* error */ librdf_log(statement->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Unknown statement part %d", context->want); node=NULL; } context->object_node=librdf_new_node_from_node(node); } node=context->object_node; break; case LIBRDF_ITERATOR_GET_METHOD_GET_CONTEXT: if(!context->context_node) { node=(librdf_node*)librdf_stream_get_context(context->stream); context->context_node=node ? librdf_new_node_from_node(node) : NULL; } node=context->context_node; break; default: librdf_log(statement->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Unknown iterator method flag %d", flags); node=NULL; } return (void*)node; } static void librdf_storage_stream_to_node_iterator_finished(void* iterator) { librdf_storage_stream_to_node_iterator_context* context=(librdf_storage_stream_to_node_iterator_context*)iterator; librdf_statement *partial_statement=context->partial_statement; if(partial_statement) librdf_free_statement(partial_statement); if(context->stream) librdf_free_stream(context->stream); if(context->storage) librdf_storage_remove_reference(context->storage); if(context->object_node) librdf_free_node(context->object_node); if(context->context_node) librdf_free_node(context->context_node); LIBRDF_FREE(librdf_storage_stream_to_node_iterator_context, context); } /* * librdf_storage_node_stream_to_node_create - Create a stream for get sources, targets or arcs methods using find_statements method * @storage: the storage object to use * @node1: the first node to encode in the key (or NULL if not needed) * @node2: the second node to encode in the key (or NULL if not needed) * @want: the field required from the statement * * node1 and node2 cannot both be NULL * * Return value: a new #librdf_iterator or NULL on failure **/ static librdf_iterator* librdf_storage_node_stream_to_node_create(librdf_storage* storage, librdf_node *node1, librdf_node *node2, librdf_statement_part want) { librdf_statement *partial_statement; librdf_stream *stream; librdf_storage_stream_to_node_iterator_context* context; librdf_iterator* iterator; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_RETURN(node1 == NULL && node2 == NULL, "both node objects are NULL", NULL); partial_statement=librdf_new_statement(storage->world); if(!partial_statement) return NULL; context=(librdf_storage_stream_to_node_iterator_context*)LIBRDF_CALLOC(librdf_storage_stream_to_node_iterator_context, 1, sizeof(librdf_storage_stream_to_node_iterator_context)); if(!context) { librdf_free_statement(partial_statement); return NULL; } if(node1) node1=librdf_new_node_from_node(node1); if(node2) node2=librdf_new_node_from_node(node2); switch(want) { case LIBRDF_STATEMENT_SUBJECT: librdf_statement_set_predicate(partial_statement, node1); librdf_statement_set_object(partial_statement, node2); break; case LIBRDF_STATEMENT_PREDICATE: librdf_statement_set_subject(partial_statement, node1); librdf_statement_set_object(partial_statement, node2); break; case LIBRDF_STATEMENT_OBJECT: librdf_statement_set_subject(partial_statement, node1); librdf_statement_set_predicate(partial_statement, node2); break; case LIBRDF_STATEMENT_ALL: default: librdf_free_node(node1); librdf_free_node(node2); librdf_free_statement(partial_statement); librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Illegal statement part %d seen", want); return NULL; } stream=storage->factory->find_statements(storage, partial_statement); if(!stream) { librdf_storage_stream_to_node_iterator_finished(context); return librdf_new_empty_iterator(storage->world); } /* initialise context */ context->partial_statement=partial_statement; context->stream=stream; context->want=want; context->storage=storage; librdf_storage_add_reference(context->storage); iterator=librdf_new_iterator(storage->world, (void*)context, librdf_storage_stream_to_node_iterator_is_end, librdf_storage_stream_to_node_iterator_next_method, librdf_storage_stream_to_node_iterator_get_method, librdf_storage_stream_to_node_iterator_finished); if(!iterator) librdf_storage_stream_to_node_iterator_finished(context); return iterator; } /** * librdf_storage_get_sources: * @storage: #librdf_storage object * @arc: #librdf_node arc * @target: #librdf_node target * * Return the sources (subjects) of arc in an RDF graph given arc (predicate) and target (object). * * Searches the storage for arcs matching the given arc and target * and returns a list of the source #librdf_node objects as an iterator * * Return value: #librdf_iterator of #librdf_node objects (may be empty) or NULL on failure **/ librdf_iterator* librdf_storage_get_sources(librdf_storage *storage, librdf_node *arc, librdf_node *target) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(arc, librdf_node, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(target, librdf_node, NULL); if (storage->factory->find_sources) return storage->factory->find_sources(storage, arc, target); return librdf_storage_node_stream_to_node_create(storage, arc, target, LIBRDF_STATEMENT_SUBJECT); } /** * librdf_storage_get_arcs: * @storage: #librdf_storage object * @source: #librdf_node source * @target: #librdf_node target * * Return the arcs (predicates) of an arc in an RDF graph given source (subject) and target (object). * * Searches the storage for arcs matching the given source and target * and returns a list of the arc #librdf_node objects as an iterator * * Return value: #librdf_iterator of #librdf_node objects (may be empty) or NULL on failure **/ librdf_iterator* librdf_storage_get_arcs(librdf_storage *storage, librdf_node *source, librdf_node *target) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(source, librdf_node, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(target, librdf_node, NULL); if (storage->factory->find_arcs) return storage->factory->find_arcs(storage, source, target); return librdf_storage_node_stream_to_node_create(storage, source, target, LIBRDF_STATEMENT_PREDICATE); } /** * librdf_storage_get_targets: * @storage: #librdf_storage object * @source: #librdf_node source * @arc: #librdf_node arc * * Return the targets (objects) of an arc in an RDF graph given source (subject) and arc (predicate). * * Searches the storage for targets matching the given source and arc * and returns a list of the source #librdf_node objects as an iterator * * Return value: #librdf_iterator of #librdf_node objects (may be empty) or NULL on failure **/ librdf_iterator* librdf_storage_get_targets(librdf_storage *storage, librdf_node *source, librdf_node *arc) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(source, librdf_node, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(arc, librdf_node, NULL); if (storage->factory->find_targets) return storage->factory->find_targets(storage, source, arc); return librdf_storage_node_stream_to_node_create(storage, source, arc, LIBRDF_STATEMENT_OBJECT); } /** * librdf_storage_get_arcs_in: * @storage: #librdf_storage object * @node: #librdf_node resource node * * Return the properties pointing to the given resource. * * Return value: #librdf_iterator of #librdf_node objects (may be empty) or NULL on failure **/ librdf_iterator* librdf_storage_get_arcs_in(librdf_storage *storage, librdf_node *node) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(node, librdf_node, NULL); if (storage->factory->get_arcs_in) return storage->factory->get_arcs_in(storage, node); return librdf_storage_node_stream_to_node_create(storage, NULL, node, LIBRDF_STATEMENT_PREDICATE); } /** * librdf_storage_get_arcs_out: * @storage: #librdf_storage object * @node: #librdf_node resource node * * Return the properties pointing from the given resource. * * Return value: #librdf_iterator of #librdf_node objects (may be empty) or NULL on failure **/ librdf_iterator* librdf_storage_get_arcs_out(librdf_storage *storage, librdf_node *node) { if (storage->factory->get_arcs_out) return storage->factory->get_arcs_out(storage, node); return librdf_storage_node_stream_to_node_create(storage, node, NULL, LIBRDF_STATEMENT_PREDICATE); } /** * librdf_storage_has_arc_in: * @storage: #librdf_storage object * @node: #librdf_node resource node * @property: #librdf_node property node * * Check if a node has a given property pointing to it. * * Return value: non 0 if arc property does point to the resource node **/ int librdf_storage_has_arc_in(librdf_storage *storage, librdf_node *node, librdf_node *property) { librdf_iterator *iterator; int status; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(node, librdf_node, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(property, librdf_node, 0); if (storage->factory->has_arc_in) return storage->factory->has_arc_in(storage, node, property); iterator=librdf_storage_get_sources(storage, property, node); if(!iterator) return 0; /* a non-empty list of sources is success */ status=!librdf_iterator_end(iterator); librdf_free_iterator(iterator); return status; } /** * librdf_storage_has_arc_out: * @storage: #librdf_storage object * @node: #librdf_node resource node * @property: #librdf_node property node * * Check if a node has a given property pointing from it. * * Return value: non 0 if arc property does point from the resource node **/ int librdf_storage_has_arc_out(librdf_storage *storage, librdf_node *node, librdf_node *property) { librdf_iterator *iterator; int status; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(node, librdf_node, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(property, librdf_node, 0); if (storage->factory->has_arc_out) return storage->factory->has_arc_out(storage, node, property); iterator=librdf_storage_get_targets(storage, node, property); if(!iterator) return 0; /* a non-empty list of targets is success */ status=!librdf_iterator_end(iterator); librdf_free_iterator(iterator); return status; } /** * librdf_storage_context_add_statement: * @storage: #librdf_storage object * @context: #librdf_node context node * @statement: #librdf_statement statement to add * * Add a statement to a storage in a context. * * If @context is NULL, this is equivalent to librdf_storage_add_statement * * Return value: non 0 on failure **/ int librdf_storage_context_add_statement(librdf_storage* storage, librdf_node* context, librdf_statement* statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, 1); if(!context) return librdf_storage_add_statement(storage, statement); if(storage->factory->context_add_statement) return storage->factory->context_add_statement(storage, context, statement); return 1; } /** * librdf_storage_context_add_statements: * @storage: #librdf_storage object * @context: #librdf_node context * @stream: #librdf_stream stream object * * Add statements to a storage with a context. * * If @context is NULL, this is equivalent to librdf_storage_add_statements * * Return value: Non 0 on failure **/ int librdf_storage_context_add_statements(librdf_storage* storage, librdf_node* context, librdf_stream* stream) { int status=0; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(stream, librdf_stream, 1); if(!context) return librdf_storage_add_statements(storage, stream); if(storage->factory->context_add_statements) return storage->factory->context_add_statements(storage, context, stream); if(!storage->factory->context_add_statement) return 1; if(!stream) return 1; while(!librdf_stream_end(stream)) { librdf_statement* statement=librdf_stream_get_object(stream); if(!statement) break; status=librdf_storage_context_add_statement(storage, context, statement); if(status) break; librdf_stream_next(stream); } return status; } /** * librdf_storage_context_remove_statement: * @storage: #librdf_storage object * @context: #librdf_node context node * @statement: #librdf_statement statement to remove * * Remove a statement from a storage in a context. * * If @context is NULL, this is equivalent to librdf_storage_remove_statement * * Return value: non 0 on failure **/ int librdf_storage_context_remove_statement(librdf_storage* storage, librdf_node* context, librdf_statement* statement) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(context, librdf_statement, 1); if(!storage->factory->context_remove_statement) return 1; return storage->factory->context_remove_statement(storage, context, statement); } /** * librdf_storage_context_remove_statements: * @storage: #librdf_storage object * @context: #librdf_uri context * * Remove statements from a storage with the given context. * * Return value: Non 0 on failure **/ int librdf_storage_context_remove_statements(librdf_storage* storage, librdf_node* context) { librdf_stream *stream; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); if(storage->factory->context_remove_statements) return storage->factory->context_remove_statements(storage, context); if(!storage->factory->context_remove_statement) return 1; stream=librdf_storage_context_as_stream(storage, context); if(!stream) return 1; while(!librdf_stream_end(stream)) { librdf_statement *statement=librdf_stream_get_object(stream); if(!statement) break; librdf_storage_context_remove_statement(storage, context, statement); librdf_stream_next(stream); } librdf_free_stream(stream); return 0; } /** * librdf_storage_context_as_stream: * @storage: #librdf_storage object * @context: #librdf_node context node * * List all statements in a storage context. * * Return value: #librdf_stream of statements or NULL on failure or context is empty **/ librdf_stream* librdf_storage_context_as_stream(librdf_storage* storage, librdf_node* context) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); return storage->factory->context_serialise(storage, context); } /** * librdf_storage_context_serialise: * @storage: #librdf_storage object * @context: #librdf_node context node * * List all statements in a storage context (DEPRECATED). * * DEPRECATED to reduce confusion with the librdf_serializer class. * Please use librdf_storage_context_as_stream. * * Return value: #librdf_stream of statements or NULL on failure or context is empty **/ librdf_stream* librdf_storage_context_serialise(librdf_storage* storage, librdf_node* context) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); return librdf_storage_context_as_stream(storage, context); } /** * librdf_storage_supports_query: * @storage: #librdf_storage object * @query: #librdf_query query object * * Check if a storage system supports a query language. * * Not implemented. * * Return value: non-0 if the query is supported. **/ int librdf_storage_supports_query(librdf_storage* storage, librdf_query *query) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 0); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(query, librdf_query, 0); return 0; } /** * librdf_storage_query_execute: * @storage: #librdf_storage object * @query: #librdf_query query object * * Run the given query against the storage. * * Not implemented. * * Return value: #librdf_query_results or NULL on failure **/ librdf_query_results* librdf_storage_query_execute(librdf_storage* storage, librdf_query *query) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(query, librdf_query, NULL); return NULL; } /** * librdf_storage_sync: * @storage: #librdf_storage object * * Synchronise the storage to the storage implementation. * * Return value: non-0 on failure **/ int librdf_storage_sync(librdf_storage* storage) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, 1); if(storage->factory->sync) return storage->factory->sync(storage); return 0; } /** * librdf_storage_find_statements_in_context: * @storage: #librdf_storage object * @statement: #librdf_statement partial statement to find * @context_node: context #librdf_node (or NULL) * * Search the storage for matching statements in a given context. * * Searches the storage for a (partial) statement as described in * librdf_statement_match() in the given context and returns a * #librdf_stream of matching #librdf_statement objects. If * context is NULL, this is equivalent to librdf_storage_find_statements. * * Return value: #librdf_stream of matching statements (may be empty) or NULL on failure **/ librdf_stream* librdf_storage_find_statements_in_context(librdf_storage* storage, librdf_statement* statement, librdf_node* context_node) { librdf_stream *stream; LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(statement, librdf_statement, NULL); if(storage->factory->find_statements_in_context) return storage->factory->find_statements_in_context(storage, statement, context_node); statement=librdf_new_statement_from_statement(statement); if(!statement) return NULL; stream=librdf_storage_context_as_stream(storage, context_node); if(!stream) { librdf_free_statement(statement); return NULL; } librdf_stream_add_map(stream, &librdf_stream_statement_find_map, (librdf_stream_map_free_context_handler)&librdf_free_statement, (void*)statement); return stream; } /** * librdf_storage_get_contexts: * @storage: #librdf_storage object * * Return the list of contexts in the store. * * Returns an iterator of #librdf_node context nodes for each * context in the store. * * Return value: #librdf_iterator of context nodes or NULL on failure or if contexts are not supported **/ librdf_iterator* librdf_storage_get_contexts(librdf_storage* storage) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); if(storage->factory->get_contexts) return storage->factory->get_contexts(storage); else return NULL; } /** * librdf_storage_get_feature: * @storage: #librdf_storage object * @feature: #librdf_uri feature property * * Get the value of a storage feature. * * Return value: new #librdf_node feature value or NULL if no such feature * exists or the value is empty. **/ librdf_node* librdf_storage_get_feature(librdf_storage* storage, librdf_uri* feature) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, NULL); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(feature, librdf_uri, NULL); if(storage->factory->get_feature) return storage->factory->get_feature(storage, feature); return NULL; } /** * librdf_storage_set_feature: * @storage: #librdf_storage object * @feature: #librdf_uri feature property * @value: #librdf_node feature property value * * Set the value of a storage feature. * * Return value: non 0 on failure (negative if no such feature) **/ int librdf_storage_set_feature(librdf_storage* storage, librdf_uri* feature, librdf_node* value) { LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(storage, librdf_storage, -1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(feature, librdf_uri, -1); LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(value, librdf_node, -1); if(storage->factory->set_feature) return storage->factory->set_feature(storage, feature, value); return -1; } /** * librdf_storage_find_statements_with_options: * @storage: #librdf_storage object * @statement: #librdf_statement partial statement to find * @context_node: #librdf_node context node or NULL. * @options: #librdf_hash of matching options or NULL * * Search the storage for matching statements with match options. * * Searches the storage for a (partial) statement as described in * librdf_statement_match() and returns a #librdf_stream of * matching #librdf_statement objects. * * If options is given then the match is made according to * the given options. If options is NULL, this is equivalent * to librdf_storage_find_statements_in_context. * * Return value: #librdf_stream of matching statements (may be empty) or NULL on failure **/ librdf_stream* librdf_storage_find_statements_with_options(librdf_storage* storage, librdf_statement* statement, librdf_node* context_node, librdf_hash* options) { if(storage->factory->find_statements_with_options) return storage->factory->find_statements_with_options(storage, statement, context_node, options); else return librdf_storage_find_statements_in_context(storage, statement, context_node); } /** * librdf_storage_transaction_start: * @storage: the storage object * * Start a transaction * * Return value: non-0 on failure **/ int librdf_storage_transaction_start(librdf_storage* storage) { if(storage->factory->transaction_start) return storage->factory->transaction_start(storage); else return 1; } /** * librdf_storage_transaction_start_with_handle: * @storage: the storage object * @handle: the transaction object * * Start a transaction using an existing external transaction object. * * Return value: non-0 on failure **/ int librdf_storage_transaction_start_with_handle(librdf_storage* storage, void* handle) { if(storage->factory->transaction_start_with_handle) return storage->factory->transaction_start_with_handle(storage, handle); else return 1; } /** * librdf_storage_transaction_commit: * @storage: the storage object * * Commit a transaction. * * Return value: non-0 on failure **/ int librdf_storage_transaction_commit(librdf_storage* storage) { if(storage->factory->transaction_commit) return storage->factory->transaction_commit(storage); else return 1; } /** * librdf_storage_transaction_rollback: * @storage: the storage object * * Rollback a transaction. * * Return value: non-0 on failure **/ int librdf_storage_transaction_rollback(librdf_storage* storage) { if(storage->factory->transaction_rollback) return storage->factory->transaction_rollback(storage); else return 1; } /** * librdf_storage_transaction_get_handle: * @storage: the storage object * * Get the current transaction handle. * * Return value: non-0 on failure **/ void* librdf_storage_transaction_get_handle(librdf_storage* storage) { if(storage->factory->transaction_get_handle) return storage->factory->transaction_get_handle(storage); else return NULL; } #endif /* TEST CODE */ #ifdef STANDALONE /* one more prototype */ int main(int argc, char *argv[]); int main(int argc, char *argv[]) { librdf_storage* storage; const char *program=librdf_basename((const char*)argv[0]); librdf_world *world; /* triples of arguments to librdf_new_storage */ const char* const storages[] = { "memory", NULL, "contexts='yes'", "hashes", "test", "hash-type='bdb',dir='.',write='yes',new='yes',contexts='yes'", #ifdef STORAGE_TREES "trees", "test", "contexts='yes'", #endif #ifdef STORAGE_FILE "file", "file://../redland.rdf", NULL, "uri", "http://librdf.org/redland.rdf", NULL, #endif #ifdef STORAGE_MYSQL "mysql", "test", "host='localhost',database='test'", #endif #ifdef STORAGE_POSTGRESQL "postgresql", "test", "host='localhost',database='test'", #endif #ifdef STORAGE_TSTORE "tstore", "test", "host='localhost',database='test'", #endif #ifdef STORAGE_SQLITE "sqlite", "test", "new='yes'", #endif NULL, NULL, NULL }; int test = 0; int ret = 0; world=librdf_new_world(); librdf_world_open(world); for ( ; storages[test] != NULL; test += 3) { fprintf(stdout, "%s: Creating storage %s\n", program, storages[test]); storage=librdf_new_storage(world, storages[test], /* type */ storages[test+1], /* name */ storages[test+2]); /* options */ if(!storage) { fprintf(stderr, "%s: WARNING: Failed to create new storage %s\n", program, storages[test]); continue; } fprintf(stdout, "%s: Opening storage\n", program); if(librdf_storage_open(storage, NULL)) { fprintf(stderr, "%s: Failed to open storage type %s\n", program, storages[test]); ret++; continue; } /* Can do nothing here since need model and storage working */ fprintf(stdout, "%s: Closing storage\n", program); librdf_storage_close(storage); fprintf(stdout, "%s: Freeing storage\n", program); librdf_free_storage(storage); } librdf_free_world(world); return ret; } #endif