mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-04 17:49:45 +02:00
1074 lines
26 KiB
C
1074 lines
26 KiB
C
/* -*- Mode: c; c-basic-offset: 2 -*-
|
|
*
|
|
* rdf_hash_bdb.c - RDF hash Berkeley DB Interface Implementation
|
|
*
|
|
* Copyright (C) 2000-2008, David Beckett http://www.dajobe.org/
|
|
* Copyright (C) 2000-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 <rdf_config.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include <win32_rdf_config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
/* for the memory allocation functions */
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_DB_H
|
|
#include <db.h>
|
|
#endif
|
|
|
|
#include <redland.h>
|
|
#include <rdf_hash.h>
|
|
|
|
|
|
typedef struct
|
|
{
|
|
librdf_hash *hash;
|
|
int mode;
|
|
int is_writable;
|
|
int is_new;
|
|
/* for BerkeleyDB only */
|
|
DB* db;
|
|
char* file_name;
|
|
} librdf_hash_bdb_context;
|
|
|
|
|
|
/* Implementing the hash cursor */
|
|
static int librdf_hash_bdb_cursor_init(void *cursor_context, void *hash_context);
|
|
static int librdf_hash_bdb_cursor_get(void *context, librdf_hash_datum* key, librdf_hash_datum* value, unsigned int flags);
|
|
static void librdf_hash_bdb_cursor_finish(void* context);
|
|
|
|
|
|
/* prototypes for local functions */
|
|
static int librdf_hash_bdb_create(librdf_hash* hash, void* context);
|
|
static int librdf_hash_bdb_destroy(void* context);
|
|
static int librdf_hash_bdb_open(void* context, const char *identifier, int mode, int is_writable, int is_new, librdf_hash* options);
|
|
static int librdf_hash_bdb_close(void* context);
|
|
static int librdf_hash_bdb_clone(librdf_hash* new_hash, void *new_context, char *new_identifier, void* old_context);
|
|
static int librdf_hash_bdb_values_count(void *context);
|
|
static int librdf_hash_bdb_put(void* context, librdf_hash_datum *key, librdf_hash_datum *data);
|
|
static int librdf_hash_bdb_exists(void* context, librdf_hash_datum *key, librdf_hash_datum *value);
|
|
static int librdf_hash_bdb_delete_key(void* context, librdf_hash_datum *key);
|
|
static int librdf_hash_bdb_delete_key_value(void* context, librdf_hash_datum *key, librdf_hash_datum *value);
|
|
static int librdf_hash_bdb_sync(void* context);
|
|
static int librdf_hash_bdb_get_fd(void* context);
|
|
|
|
static void librdf_hash_bdb_register_factory(librdf_hash_factory *factory);
|
|
|
|
|
|
/* functions implementing hash api */
|
|
|
|
/**
|
|
* librdf_hash_bdb_create:
|
|
* @hash: #librdf_hash hash that this implements
|
|
* @context: BerkeleyDB hash context
|
|
*
|
|
* Create a BerkeleyDB hash.
|
|
*
|
|
* Return value: non 0 on failure.
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_create(librdf_hash* hash, void* context)
|
|
{
|
|
librdf_hash_bdb_context* hcontext=(librdf_hash_bdb_context*)context;
|
|
|
|
hcontext->hash=hash;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_destroy:
|
|
* @context: BerkeleyDB hash context
|
|
*
|
|
* Destroy a BerkeleyDB hash.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_destroy(void* context)
|
|
{
|
|
/* NOP */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_open:
|
|
* @context: BerkeleyDB hash context
|
|
* @identifier: filename to use for BerkeleyDB file
|
|
* @mode: file creation mode
|
|
* @is_writable: is hash writable?
|
|
* @is_new: is hash new?
|
|
* @options: hash options (currently unused)
|
|
*
|
|
* Open and maybe create a BerkeleyDB hash.
|
|
*
|
|
* Return value: non 0 on failure.
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_open(void* context, const char *identifier,
|
|
int mode, int is_writable, int is_new,
|
|
librdf_hash* options)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* bdb;
|
|
char *file;
|
|
int ret;
|
|
int flags;
|
|
|
|
LIBRDF_ASSERT_OBJECT_POINTER_RETURN_VALUE(identifier, cstring, 1);
|
|
|
|
#ifdef HAVE_DB_OPEN
|
|
DB_INFO bdb_info;
|
|
#endif
|
|
|
|
/* NOTE: If the options parameter is ever used here, the data must be
|
|
* copied into a private part of the context so that the clone
|
|
* method can access them
|
|
*/
|
|
bdb_context->mode=mode;
|
|
bdb_context->is_writable=is_writable;
|
|
bdb_context->is_new=is_new;
|
|
|
|
file=(char*)LIBRDF_MALLOC(cstring, strlen(identifier)+4);
|
|
if(!file)
|
|
return 1;
|
|
sprintf(file, "%s.db", identifier);
|
|
|
|
#ifdef HAVE_DB_CREATE
|
|
/* V3 prototype:
|
|
* int db_create(DB **dbp, DB_ENV *dbenv, u_int32_t flags);
|
|
*/
|
|
if((ret=db_create(&bdb, NULL, 0))) {
|
|
LIBRDF_DEBUG2("Failed to create BDB context - %d\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef HAVE_BDB_SET_FLAGS
|
|
if((ret=bdb->set_flags(bdb, DB_DUP))) {
|
|
LIBRDF_DEBUG2("Failed to set BDB duplicate flag - %d\n", ret);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/* V3 prototype:
|
|
* int DB->open(DB *db, const char *file, const char *database,
|
|
* DBTYPE type, u_int32_t flags, int mode);
|
|
*/
|
|
flags=is_writable ? DB_CREATE : DB_RDONLY;
|
|
if(is_new)
|
|
flags |= DB_TRUNCATE;
|
|
#endif
|
|
|
|
#if defined(HAVE_BDB_OPEN_6_ARGS) || defined(HAVE_BDB_OPEN_7_ARGS)
|
|
|
|
#ifdef HAVE_BDB_OPEN_6_ARGS
|
|
/*
|
|
* int DB->open(DB *db, const char *file,
|
|
* const char *database, DBTYPE type, u_int32_t flags, int mode);
|
|
*/
|
|
if((ret=bdb->open(bdb, file, NULL, DB_BTREE, flags, mode))) {
|
|
librdf_log(bdb_context->hash->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
|
|
"BDB V4.0+ open of '%s' failed - %s", file, db_strerror(ret));
|
|
LIBRDF_FREE(cstring, file);
|
|
return 1;
|
|
}
|
|
#else
|
|
/* Must be HAVE_BDB_OPEN_7_ARGS */
|
|
|
|
/*
|
|
* int DB->open(DB *db, DB_TXN *txnid, const char *file,
|
|
* const char *database, DBTYPE type, u_int32_t flags, int mode);
|
|
*/
|
|
if((ret=bdb->open(bdb, NULL, file, NULL, DB_BTREE, flags, mode))) {
|
|
librdf_log(bdb_context->hash->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
|
|
"BDB V4.1+ open of '%s' failed - %s", file, db_strerror(ret));
|
|
LIBRDF_FREE(cstring, file);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
#ifdef HAVE_DB_OPEN
|
|
/* V2 prototype:
|
|
* int db_open(const char *file, DBTYPE type, u_int32_t flags,
|
|
* int mode, DB_ENV *dbenv, DB_INFO *dbinfo, DB **dbpp);
|
|
*/
|
|
|
|
memset(&bdb_info, 0, sizeof(DB_INFO));
|
|
bdb_info.flags=DB_DUP;
|
|
|
|
flags=is_writable ? DB_CREATE : DB_RDONLY;
|
|
if(is_new)
|
|
flags |= DB_TRUNCATE;
|
|
|
|
if((ret=db_open(file, DB_BTREE, flags, mode, NULL, &bdb_info, &bdb))) {
|
|
librdf_log(bdb_context->hash->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
|
|
"BDB V2 open of '%s' failed - %d", file, ret);
|
|
LIBRDF_FREE(cstring, file);
|
|
return 1;
|
|
}
|
|
#else
|
|
#ifdef HAVE_DBOPEN
|
|
/* V1 prototype:
|
|
const char *file, int flags, int mode, DBTYPE, const void *openinfo
|
|
*/
|
|
flags=is_writable ? O_RDWR|O_CREAT : O_RDONLY
|
|
|
|
flags|=R_DUP;
|
|
|
|
/* There does not seem to be a V1 flag for truncate */
|
|
if(is_new)
|
|
remove(file);
|
|
|
|
if((bdb=dbopen(file, flags, mode, DB_BTREE, NULL)) == 0) {
|
|
librdf_log(bdb_context->hash->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
|
|
"BDB V1 open of '%s' failed - %d", file, ret);
|
|
LIBRDF_FREE(cstring, file);
|
|
return 1;
|
|
}
|
|
ret=0;
|
|
#else
|
|
#ifdef HAVE_DB_CREATE
|
|
/* earlier */
|
|
#else
|
|
ERROR - no idea how to use Berkeley DB
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
bdb_context->db=bdb;
|
|
bdb_context->file_name=file;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_close:
|
|
* @context: BerkeleyDB hash context
|
|
*
|
|
* Close the hash.
|
|
*
|
|
* Finish the association between the rdf hash and the BDB file (does
|
|
* not delete the file)
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_close(void* context)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* db=bdb_context->db;
|
|
int ret;
|
|
|
|
#ifdef HAVE_BDB_CLOSE_2_ARGS
|
|
/* V2/V3 */
|
|
ret=db->close(db, 0);
|
|
#else
|
|
/* V1 */
|
|
ret=db->close(db);
|
|
#endif
|
|
LIBRDF_FREE(cstring, bdb_context->file_name);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_clone:
|
|
* @hash: new #librdf_hash that this implements
|
|
* @context: new BerkeleyDB hash context
|
|
* @new_identifier: new identifier for this hash
|
|
* @old_context: old BerkeleyDB hash context
|
|
*
|
|
* Clone the BerkeleyDB hash.
|
|
*
|
|
* Clones the existing Berkeley DB hash into the new one with the
|
|
* new identifier.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_clone(librdf_hash *hash, void* context, char *new_identifier,
|
|
void *old_context)
|
|
{
|
|
librdf_hash_bdb_context* hcontext=(librdf_hash_bdb_context*)context;
|
|
librdf_hash_bdb_context* old_hcontext=(librdf_hash_bdb_context*)old_context;
|
|
librdf_hash_datum *key, *value;
|
|
librdf_iterator *iterator;
|
|
int status=0;
|
|
|
|
/* copy data fields that might change */
|
|
hcontext->hash=hash;
|
|
|
|
/* Note: The options are not used at present, so no need to make a copy
|
|
*/
|
|
if(librdf_hash_bdb_open(context, new_identifier,
|
|
old_hcontext->mode, old_hcontext->is_writable,
|
|
old_hcontext->is_new, NULL))
|
|
return 1;
|
|
|
|
|
|
/* Use higher level functions to iterator this data
|
|
* on the other hand, maybe this is a good idea since that
|
|
* code is tested and works
|
|
*/
|
|
|
|
key=librdf_new_hash_datum(hash->world, NULL, 0);
|
|
value=librdf_new_hash_datum(hash->world, NULL, 0);
|
|
|
|
iterator=librdf_hash_get_all(old_hcontext->hash, key, value);
|
|
while(!librdf_iterator_end(iterator)) {
|
|
librdf_hash_datum* k= (librdf_hash_datum*)librdf_iterator_get_key(iterator);
|
|
librdf_hash_datum* v= (librdf_hash_datum*)librdf_iterator_get_value(iterator);
|
|
|
|
if(librdf_hash_bdb_put(hcontext, k, v)) {
|
|
status=1;
|
|
break;
|
|
}
|
|
librdf_iterator_next(iterator);
|
|
}
|
|
if(iterator)
|
|
librdf_free_iterator(iterator);
|
|
|
|
librdf_free_hash_datum(value);
|
|
librdf_free_hash_datum(key);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_values_count:
|
|
* @context: BerkeleyDB hash context
|
|
*
|
|
* Get the number of values in the hash.
|
|
*
|
|
* Return value: number of values in the hash or <0 if not available
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_values_count(void *context)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
librdf_hash_bdb_context* hash;
|
|
void *last_key;
|
|
void *last_value;
|
|
#ifdef HAVE_BDB_CURSOR
|
|
DBC* cursor;
|
|
#endif
|
|
} librdf_hash_bdb_cursor_context;
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_cursor_init:
|
|
* @cursor_context: hash cursor context
|
|
* @hash_context: hash to operate over
|
|
*
|
|
* Initialise a new bdb cursor.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_cursor_init(void *cursor_context, void *hash_context)
|
|
{
|
|
librdf_hash_bdb_cursor_context *cursor=(librdf_hash_bdb_cursor_context*)cursor_context;
|
|
#ifdef HAVE_BDB_CURSOR
|
|
DB* db;
|
|
#endif
|
|
|
|
cursor->hash=(librdf_hash_bdb_context*)hash_context;
|
|
|
|
#ifdef HAVE_BDB_CURSOR
|
|
db=cursor->hash->db;
|
|
#ifdef HAVE_BDB_CURSOR_4_ARGS
|
|
/* V3 prototype:
|
|
* int DB->cursor(DB *db, DB_TXN *txnid, DBC **cursorp, u_int32_t flags);
|
|
*/
|
|
if(db->cursor(db, NULL, &cursor->cursor, 0))
|
|
return 1;
|
|
#else
|
|
/* V2 prototype:
|
|
* int DB->cursor(DB *db, DB_TXN *txnid, DBC **cursorp);
|
|
*/
|
|
if(db->cursor(db, NULL, &cursor->cursor))
|
|
return 1;
|
|
#endif
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_cursor_get:
|
|
* @context: BerkeleyDB hash cursor context
|
|
* @key: pointer to key to use
|
|
* @value: pointer to value to use
|
|
* @flags: flags
|
|
*
|
|
* Retrieve a hash value for the given key.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_cursor_get(void* context,
|
|
librdf_hash_datum *key, librdf_hash_datum *value,
|
|
unsigned int flags)
|
|
{
|
|
librdf_hash_bdb_cursor_context *cursor=(librdf_hash_bdb_cursor_context*)context;
|
|
#ifdef HAVE_BDB_CURSOR
|
|
DBC *bdb_cursor=cursor->cursor;
|
|
#else
|
|
/* For BDB V1 */
|
|
DB* db;
|
|
#endif
|
|
DBT bdb_key;
|
|
DBT bdb_value;
|
|
int ret;
|
|
|
|
/* docs say you must zero DBT's before use */
|
|
memset(&bdb_key, 0, sizeof(DBT));
|
|
memset(&bdb_value, 0, sizeof(DBT));
|
|
|
|
/* Always initialise BDB version of key */
|
|
bdb_key.data = (char*)key->data;
|
|
bdb_key.size = key->size;
|
|
|
|
#ifdef DB_DBT_MALLOC
|
|
/* BDB V2 or later? */
|
|
bdb_key.flags=DB_DBT_MALLOC; /* Return in malloc() allocated memory */
|
|
bdb_value.flags=DB_DBT_MALLOC;
|
|
#endif
|
|
|
|
#ifndef HAVE_BDB_CURSOR
|
|
/* For BDB V1 */
|
|
db=cursor->hash->db;
|
|
#endif
|
|
|
|
switch(flags) {
|
|
case LIBRDF_HASH_CURSOR_SET:
|
|
|
|
#ifdef HAVE_BDB_CURSOR
|
|
/* V2/V3 prototype:
|
|
* int DBcursor->c_get(DBC *cursor, DBT *key, DBT *data, u_int32_t flags);
|
|
*/
|
|
ret=bdb_cursor->c_get(bdb_cursor, &bdb_key, &bdb_value, DB_SET);
|
|
#else
|
|
/* V1 */
|
|
ret=db->seq(db, &bdb_key, &bdb_value, 0);
|
|
#endif
|
|
break;
|
|
|
|
case LIBRDF_HASH_CURSOR_FIRST:
|
|
#ifdef HAVE_BDB_CURSOR
|
|
/* V2/V3 prototype:
|
|
* int DBcursor->c_get(DBC *cursor, DBT *key, DBT *data, u_int32_t flags);
|
|
*/
|
|
ret=bdb_cursor->c_get(bdb_cursor, &bdb_key, &bdb_value, DB_FIRST);
|
|
#else
|
|
/* V1 */
|
|
ret=db->seq(db, &bdb_key, &bdb_value, R_FIRST);
|
|
#endif
|
|
break;
|
|
|
|
case LIBRDF_HASH_CURSOR_NEXT_VALUE:
|
|
#ifdef HAVE_BDB_CURSOR
|
|
/* V2/V3 */
|
|
ret=bdb_cursor->c_get(bdb_cursor, &bdb_key, &bdb_value, DB_NEXT);
|
|
#else
|
|
/* V1 */
|
|
ret=db->seq(db, &bdb_key, &bdb_value, R_NEXT);
|
|
#endif
|
|
|
|
/* If succeeded and key has changed, end */
|
|
if(!ret && cursor->last_key &&
|
|
memcmp(cursor->last_key, bdb_key.data, bdb_key.size)) {
|
|
|
|
/* always allocated by BDB using system malloc */
|
|
SYSTEM_FREE(bdb_key.data);
|
|
SYSTEM_FREE(bdb_value.data);
|
|
|
|
#ifdef DB_NOTFOUND
|
|
/* V2 and V3 */
|
|
ret=DB_NOTFOUND;
|
|
#else
|
|
ret=1;
|
|
#endif
|
|
}
|
|
|
|
break;
|
|
|
|
case LIBRDF_HASH_CURSOR_NEXT:
|
|
#ifdef HAVE_BDB_CURSOR
|
|
#ifdef DB_NEXT_NODUP
|
|
/* V3 */
|
|
|
|
/* Get next key, or next key/value (when value defined) */
|
|
ret=bdb_cursor->c_get(bdb_cursor, &bdb_key, &bdb_value,
|
|
(value) ? DB_NEXT : DB_NEXT_NODUP);
|
|
#else
|
|
/* V2 */
|
|
|
|
/* Must mess about finding next key - note this relies on
|
|
* the bdb btree having the keys in sorted order
|
|
*/
|
|
while(1) {
|
|
ret=bdb_cursor->c_get(bdb_cursor, &bdb_key, &bdb_value, DB_NEXT);
|
|
/* finish on error, want all values or no previous key */
|
|
if(ret || value || !cursor->last_key)
|
|
break;
|
|
/* else have previous key and want unique keys, so keep
|
|
* going until the key changes
|
|
*/
|
|
if(memcmp(cursor->last_key, bdb_key.data, bdb_key.size))
|
|
break;
|
|
|
|
/* always allocated by BDB using system malloc */
|
|
SYSTEM_FREE(bdb_key.data);
|
|
SYSTEM_FREE(bdb_value.data);
|
|
}
|
|
#endif
|
|
#else
|
|
/* V1 */
|
|
ret=db->seq(db, &bdb_key, &bdb_value, R_NEXT);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
librdf_log(cursor->hash->hash->world,
|
|
0, LIBRDF_LOG_ERROR, LIBRDF_FROM_HASH, NULL,
|
|
"Unknown hash method flag %d", flags);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Free previous key and values */
|
|
if(cursor->last_key) {
|
|
LIBRDF_FREE(cstring, cursor->last_key);
|
|
cursor->last_key=NULL;
|
|
}
|
|
|
|
if(cursor->last_value) {
|
|
LIBRDF_FREE(cstring, cursor->last_value);
|
|
cursor->last_value=NULL;
|
|
}
|
|
|
|
|
|
|
|
if(ret) {
|
|
#ifdef DB_NOTFOUND
|
|
/* V2 and V3 */
|
|
if(ret != DB_NOTFOUND)
|
|
LIBRDF_DEBUG2("BDB cursor error - %d\n", ret);
|
|
#endif
|
|
key->data=NULL;
|
|
return ret;
|
|
}
|
|
|
|
cursor->last_key = key->data = LIBRDF_MALLOC(cstring, bdb_key.size);
|
|
if(!key->data) {
|
|
/* always allocated by BDB using system malloc */
|
|
if(flags != LIBRDF_HASH_CURSOR_SET)
|
|
SYSTEM_FREE(bdb_key.data);
|
|
SYSTEM_FREE(bdb_value.data);
|
|
return 1;
|
|
}
|
|
|
|
memcpy(key->data, bdb_key.data, bdb_key.size);
|
|
key->size = bdb_key.size;
|
|
|
|
if(value) {
|
|
cursor->last_value = value->data = LIBRDF_MALLOC(cstring, bdb_value.size);
|
|
if(!value->data) {
|
|
/* always allocated by BDB using system malloc */
|
|
if(flags != LIBRDF_HASH_CURSOR_SET)
|
|
SYSTEM_FREE(bdb_key.data);
|
|
SYSTEM_FREE(bdb_value.data);
|
|
return 1;
|
|
}
|
|
|
|
memcpy(value->data, bdb_value.data, bdb_value.size);
|
|
value->size = bdb_value.size;
|
|
}
|
|
|
|
/* always allocated by BDB using system malloc */
|
|
if(flags != LIBRDF_HASH_CURSOR_SET)
|
|
SYSTEM_FREE(bdb_key.data);
|
|
SYSTEM_FREE(bdb_value.data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_cursor_finished:
|
|
* @context: BerkeleyDB hash cursor context
|
|
*
|
|
* Finish the serialisation of the hash bdb get.
|
|
*
|
|
**/
|
|
static void
|
|
librdf_hash_bdb_cursor_finish(void* context)
|
|
{
|
|
librdf_hash_bdb_cursor_context* cursor=(librdf_hash_bdb_cursor_context*)context;
|
|
|
|
#ifdef HAVE_BDB_CURSOR
|
|
/* BDB V2/V3 */
|
|
if(cursor->cursor)
|
|
cursor->cursor->c_close(cursor->cursor);
|
|
#endif
|
|
if(cursor->last_key)
|
|
LIBRDF_FREE(cstring, cursor->last_key);
|
|
|
|
if(cursor->last_value)
|
|
LIBRDF_FREE(cstring, cursor->last_value);
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_put:
|
|
* @context: BerkeleyDB hash context
|
|
* @key: pointer to key to store
|
|
* @value: pointer to value to store
|
|
*
|
|
* Store a key/value pair in the hash.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_put(void* context, librdf_hash_datum *key,
|
|
librdf_hash_datum *value)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* db=bdb_context->db;
|
|
DBT bdb_value;
|
|
DBT bdb_key;
|
|
int ret;
|
|
|
|
/* docs say you must zero DBT's before use */
|
|
memset(&bdb_value, 0, sizeof(DBT));
|
|
memset(&bdb_key, 0, sizeof(DBT));
|
|
|
|
/* Initialise BDB version of key */
|
|
bdb_key.data = (char*)key->data;
|
|
bdb_key.size = key->size;
|
|
|
|
/* Initialise BDB version of data */
|
|
bdb_value.data = (char*)value->data;
|
|
bdb_value.size = value->size;
|
|
|
|
#ifdef HAVE_BDB_DB_TXN
|
|
/* V2/V3 prototype:
|
|
* int DB->put(DB *db, DB_TXN *txnid, DBT *key, DBT *data, u_int32_t flags);
|
|
*/
|
|
ret=db->put(db, NULL, &bdb_key, &bdb_value, 0);
|
|
#else
|
|
/* V1 */
|
|
ret=db->put(db, &bdb_key, &bdb_value, 0);
|
|
#endif
|
|
if(ret)
|
|
LIBRDF_DEBUG2("BDB put failed - %d\n", ret);
|
|
|
|
return (ret != 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_exists:
|
|
* @context: BerkeleyDB hash context
|
|
* @key: pointer to key
|
|
* @value: pointer to value (optional)
|
|
*
|
|
* Test the existence of a key/value in the hash.
|
|
*
|
|
* The value can be NULL in which case the check will just be
|
|
* for the key.
|
|
*
|
|
* Return value: >0 if the key/value exists in the hash, 0 if not, <0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_exists(void* context, librdf_hash_datum *key,
|
|
librdf_hash_datum *value)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* db=bdb_context->db;
|
|
DBT bdb_key;
|
|
DBT bdb_value;
|
|
int ret;
|
|
|
|
/* docs say you must zero DBT's before use */
|
|
memset(&bdb_key, 0, sizeof(DBT));
|
|
memset(&bdb_value, 0, sizeof(DBT));
|
|
|
|
/* Initialise BDB version of key */
|
|
bdb_key.data = (char*)key->data;
|
|
bdb_key.size = key->size;
|
|
|
|
if(value) {
|
|
bdb_value.data = (char*)value->data;
|
|
bdb_value.size = value->size;
|
|
}
|
|
|
|
#ifdef HAVE_BDB_DB_TXN
|
|
#ifdef DB_GET_BOTH
|
|
/* later V2 (sigh)/V3 */
|
|
ret=db->get(db, NULL, &bdb_key, &bdb_value, (value ? DB_GET_BOTH : 0));
|
|
if(ret == DB_NOTFOUND)
|
|
ret= 0;
|
|
else if(ret) /* failed */
|
|
ret= -1;
|
|
else
|
|
ret= 1;
|
|
#else
|
|
/* earlier V2 */
|
|
if(!value) {
|
|
/* don't care about value, can use standard get */
|
|
ret=db->get(db, NULL, &bdb_key, &bdb_value, 0);
|
|
if(ret == DB_NOTFOUND)
|
|
ret= 0;
|
|
else if(ret) /* failed */
|
|
ret= -1;
|
|
else
|
|
ret= 1;
|
|
} else {
|
|
/* Want exact key/value - have to use a cursor, darn */
|
|
DBC* dbc=NULL;
|
|
|
|
ret=1;
|
|
|
|
if(db->cursor(db, NULL, &dbc))
|
|
ret= -1;
|
|
|
|
if(ret >= 0) {
|
|
ret=dbc->c_get(dbc, &bdb_key, &bdb_value, DB_SET);
|
|
if(ret == DB_NOTFOUND)
|
|
ret= 0;
|
|
else if(ret) /* failed */
|
|
ret= -1;
|
|
else
|
|
ret= 1;
|
|
}
|
|
|
|
while(ret > 0) {
|
|
/* key different - match failed */
|
|
if(memcmp(key->data, bdb_key.data, key->size)) {
|
|
ret=0;
|
|
break;
|
|
}
|
|
/* value equal - match found */
|
|
if(!memcmp(value->data, bdb_value.data, value->size)) {
|
|
ret=1;
|
|
break;
|
|
}
|
|
ret=dbc->c_get(dbc, &bdb_key, &bdb_value, DB_NEXT);
|
|
if(ret == DB_NOTFOUND)
|
|
ret= 0;
|
|
else if(ret) /* failed */
|
|
ret= -1;
|
|
else
|
|
ret= 1;
|
|
}
|
|
if(dbc)
|
|
dbc->c_close(dbc);
|
|
}
|
|
#endif
|
|
#else
|
|
/* V1 */
|
|
if(!value) {
|
|
/* don't care about value, can use standard get */
|
|
ret=db->get(db, &bdb_key, &bdb_value, 0);
|
|
if(ret >0) /* not found */
|
|
ret= 0;
|
|
else if(ret <0) /* failed */
|
|
ret= -1;
|
|
else /* 0 = found */
|
|
ret= 1;
|
|
} else {
|
|
/* Want exact key/value - have to use sequence */
|
|
|
|
ret=db->seq(bdb, &bdb_key, &bdb_value, 0);
|
|
if(ret) /* failed */
|
|
ret= -1;
|
|
else
|
|
ret= 1;
|
|
|
|
while(ret > 0) {
|
|
/* key different - match failed */
|
|
if(memcmp(key->data, bdb_key.data, key->size)) {
|
|
ret=0;
|
|
break;
|
|
}
|
|
/* value equal - match found */
|
|
if(!memcmp(value->data, bdb_value.data, value->size)) {
|
|
ret=1;
|
|
break;
|
|
}
|
|
ret=db->seq(dbc, &bdb_key, &bdb_value, R_NEXT);
|
|
if(ret) /* not found */
|
|
ret= 0;
|
|
else
|
|
ret= 1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_delete_key:
|
|
* @context: BerkeleyDB hash context
|
|
* @key: key
|
|
*
|
|
* Delete all values for given key from the hash.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_delete_key(void* context, librdf_hash_datum *key)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* bdb=bdb_context->db;
|
|
DBT bdb_key;
|
|
int ret;
|
|
|
|
memset(&bdb_key, 0, sizeof(DBT));
|
|
|
|
/* Initialise BDB version of key */
|
|
bdb_key.data = (char*)key->data;
|
|
bdb_key.size = key->size;
|
|
|
|
#ifdef HAVE_BDB_DB_TXN
|
|
/* V2/V3 */
|
|
ret=bdb->del(bdb, NULL, &bdb_key, 0);
|
|
#else
|
|
/* V1 */
|
|
ret=bdb->del(bdb, &bdb_key, 0);
|
|
#endif
|
|
if(ret)
|
|
LIBRDF_DEBUG2("BDB del failed - %d\n", ret);
|
|
|
|
return (ret != 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_delete_key_value:
|
|
* @context: BerkeleyDB hash context
|
|
* @key: key
|
|
* @value: value
|
|
*
|
|
* Delete given key/value from the hash.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_delete_key_value(void* context,
|
|
librdf_hash_datum *key, librdf_hash_datum *value)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* bdb=bdb_context->db;
|
|
DBT bdb_key, bdb_value;
|
|
int ret;
|
|
#ifdef HAVE_BDB_CURSOR
|
|
DBC* dbc;
|
|
#endif
|
|
|
|
memset(&bdb_key, 0, sizeof(DBT));
|
|
memset(&bdb_value, 0, sizeof(DBT));
|
|
|
|
/* Initialise BDB version of key */
|
|
bdb_key.data = (char*)key->data;
|
|
bdb_key.size = key->size;
|
|
|
|
bdb_value.data = (char*)value->data;
|
|
bdb_value.size = value->size;
|
|
|
|
#ifdef HAVE_BDB_CURSOR
|
|
#ifdef HAVE_BDB_CURSOR_4_ARGS
|
|
/* V3 prototype:
|
|
* int DB->cursor(DB *db, DB_TXN *txnid, DBC **cursorp, u_int32_t flags);
|
|
*/
|
|
if(bdb->cursor(bdb, NULL, &dbc, 0))
|
|
return 1;
|
|
#else
|
|
/* V2 prototype:
|
|
* int DB->cursor(DB *db, DB_TXN *txnid, DBC **cursorp);
|
|
*/
|
|
if(bdb->cursor(bdb, NULL, &dbc))
|
|
return 1;
|
|
#endif
|
|
|
|
/* V2/V3 prototype:
|
|
* int DBcursor->c_get(DBC *cursor, DBT *key, DBT *data, u_int32_t flags);
|
|
*/
|
|
#ifdef DB_GET_BOTH
|
|
/* later V2 (sigh) / V3 */
|
|
ret=dbc->c_get(dbc, &bdb_key, &bdb_value, DB_GET_BOTH);
|
|
#else
|
|
/* earlier V2 probably gives a memory leak */
|
|
ret=dbc->c_get(dbc, &bdb_key, &bdb_value, 0);
|
|
#endif
|
|
if(ret) {
|
|
dbc->c_close(dbc);
|
|
return 1;
|
|
}
|
|
|
|
/* finally - delete the sucker */
|
|
ret=dbc->c_del(dbc, 0);
|
|
|
|
dbc->c_close(dbc);
|
|
#else
|
|
/* V1 prototype:
|
|
* int db->seq(DB* db, DBT *key, DBT *data, u_int flags);
|
|
*/
|
|
ret=bdb->seq(bdb, &bdb_key, &bdb_value, 0);
|
|
if(ret)
|
|
return 1;
|
|
|
|
/* V1 prototype:
|
|
* int db->del(DB* db, DBT *key, u_int flags);
|
|
*/
|
|
ret=bdb->del(bdb, &bdb_key, R_CURSOR);
|
|
#endif
|
|
|
|
if(ret)
|
|
LIBRDF_DEBUG2("BDB del failed - %d\n", ret);
|
|
|
|
return (ret != 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_sync:
|
|
* @context: BerkeleyDB hash context
|
|
*
|
|
* Flush the hash to disk.
|
|
*
|
|
* Return value: non 0 on failure
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_sync(void* context)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* db=bdb_context->db;
|
|
int ret;
|
|
|
|
return (ret=db->sync(db, 0));
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_hash_bdb_get_fd:
|
|
* @context: BerkeleyDB hash context
|
|
*
|
|
* Get the file description representing the hash.
|
|
*
|
|
* Return value: the file descriptor
|
|
**/
|
|
static int
|
|
librdf_hash_bdb_get_fd(void* context)
|
|
{
|
|
librdf_hash_bdb_context* bdb_context=(librdf_hash_bdb_context*)context;
|
|
DB* db=bdb_context->db;
|
|
int fd;
|
|
int ret;
|
|
|
|
#ifdef HAVE_BDB_FD_2_ARGS
|
|
ret=db->fd(db, &fd);
|
|
#else
|
|
ret=0;
|
|
fd=db->fd(db);
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
|
|
/* local function to register BDB hash functions */
|
|
|
|
/**
|
|
* librdf_hash_bdb_register_factory:
|
|
* @factory: hash factory prototype
|
|
*
|
|
* Register the BerkeleyDB hash module with the hash factory.
|
|
*
|
|
**/
|
|
static void
|
|
librdf_hash_bdb_register_factory(librdf_hash_factory *factory)
|
|
{
|
|
factory->context_length = sizeof(librdf_hash_bdb_context);
|
|
factory->cursor_context_length = sizeof(librdf_hash_bdb_cursor_context);
|
|
|
|
factory->create = librdf_hash_bdb_create;
|
|
factory->destroy = librdf_hash_bdb_destroy;
|
|
|
|
factory->open = librdf_hash_bdb_open;
|
|
factory->close = librdf_hash_bdb_close;
|
|
factory->clone = librdf_hash_bdb_clone;
|
|
|
|
factory->values_count = librdf_hash_bdb_values_count;
|
|
|
|
factory->put = librdf_hash_bdb_put;
|
|
factory->exists = librdf_hash_bdb_exists;
|
|
factory->delete_key = librdf_hash_bdb_delete_key;
|
|
factory->delete_key_value = librdf_hash_bdb_delete_key_value;
|
|
factory->sync = librdf_hash_bdb_sync;
|
|
factory->get_fd = librdf_hash_bdb_get_fd;
|
|
|
|
factory->cursor_init = librdf_hash_bdb_cursor_init;
|
|
factory->cursor_get = librdf_hash_bdb_cursor_get;
|
|
factory->cursor_finish = librdf_hash_bdb_cursor_finish;
|
|
}
|
|
|
|
|
|
/**
|
|
* librdf_init_hash_bdb:
|
|
* @world: redland world object
|
|
*
|
|
* Initialise the BerkeleyDB hash module.
|
|
*
|
|
**/
|
|
void
|
|
librdf_init_hash_bdb(librdf_world *world)
|
|
{
|
|
librdf_hash_register_factory(world,
|
|
"bdb", &librdf_hash_bdb_register_factory);
|
|
}
|