/*!
@header BDBDatabase
@abstract Contains a class representing a database
@availability OS X, GNUstep
@copyright (C) 2004, 2005, 2006 Oliver Langer
Author: Oliver Langer
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-------------------------------------------------------------------------
Modification history
09.12.2005 ola initial version
22.08.2006 ola license changed
-------------------------------------------------------------------------
******************************************************************************/
#include
#include
#include "BDBExceptions.h"
#include
#include "BDBErrorHandling.h"
#include
#include
static ECLogger *_log;
@implementation BDBDatabase
+ (BDBDatabase *) initWithFilename: (NSString *) fileName
databaseName: (NSString *) aDatabaseName
databaseConfig: (BDBDatabaseConfig *) aDbConfig {
BDBDatabase *toReturn = nil;
int result;
toReturn = [ [ BDBDatabase alloc ] init ];
toReturn->dbConfig = [ aDbConfig retain ];
toReturn->databaseName = [ aDatabaseName retain ];
toReturn->db = [aDbConfig db];
result = toReturn->db->open( toReturn->db, NULL, [ fileName cString ],
(aDatabaseName != nil ) ? [ aDatabaseName cString ] : NULL,
[ toReturn->dbConfig databaseType ], [ toReturn->dbConfig openFlags ], 0 );
return toReturn;
}
+ (void) initialize {
_log = [[ECLogging loggerForContext: LOGCONTEXT_BDB] retain];
}
- init {
self = [ super init ];
self->db = NULL;
self->databaseName = nil;
return self;
}
- (void) dealloc {
if( NULL != db ) {
if( [_log isDebugEnabled] ) {
NSString *s;
if( nil != self->databaseName ) {
s = self->databaseName;
} else {
s= @"NOT SPECIFIED";
}
[_log debug: @"Closing database name=%@", s ];
}
db->close( db, 0 );
db = NULL;
}
[ super dealloc ];
}
- (BDBOperationStatus) appendEntryWithTransaction:
(BDBTransaction *) transaction
entryToAppend: (BDBDatabaseEntryData *) entryToAppend
resultingRecordNr: (BDBDatabaseRecordNumber *) recordNr {
BDBOperationStatus status = BDB_STATUS_UNKNOWN;
NSData *dataToAppend;
NSAssert( nil == transaction, @"Transaction not supported at present!" );
if( (BDB_QUEUE != [dbConfig databaseType])
&& (BDB_RECNO != [dbConfig databaseType]) ) {
[[[ECIllegalOperationException alloc]
initWithOperationInformation: @"BDBDatabase::appendEntryWithTransaction:"\
"This operation is only defined for databases of type BDB_QUEUE and"\
" BDB_RECNO"] raise];
}
EC_AUTORELEASEPOOL_BEGIN
DBT dbKey, dbValue;
dataToAppend = [entryToAppend data];
if( nil == dataToAppend ) {
[[[ECIllegalArgumentException alloc]
initWithArgumentInfo: @"BDBDatabase::appendEntryWithTransaction:"\
"Given database entry contains no data!"] raise];
}
memset( &dbKey, 0, sizeof( DBT ) );
memset( &dbValue, 0, sizeof( DBT ) );
dbValue.data = (void *) [dataToAppend bytes];
dbValue.size = [dataToAppend length];
status = db->put( db, NULL, &dbKey, &dbValue, DB_APPEND );
if( BDB_STATUS_SUCCESS == status ) {
if( nil != recordNr ) {
NSAssert( dbKey.size == sizeof( u_int32_t ),
@"BDBDatabase::appendEntryWithTransaction: Returned key is NOT of type"\
" u_int32_t." );
[recordNr setEntryNumber: (*((u_int32_t *) dbKey.data))];
}
} else {
if( [_log isDebugEnabled] ) {
[_log debug:@"BDBDatabase::appendEntryWithTransaction: "\
"db->put returned with error code %u.", status ];
}
switch( status ) {
case DB_LOCK_DEADLOCK:
[[[BDBDeadLockException alloc] initWithErrorCode: (int) status] raise];
case DB_LOCK_NOTGRANTED:
[[[BDBLockNotGrantedException alloc]
initWithErrorCode: (int) status] raise];
case EACCES:
[[[ECIllegalOperationException alloc]
initWithOperationInformation:
@"BDBDatabase::appendEntryWithTransaction: Trying to modify a "\
"read-only database!"] raise];
case DB_REP_HANDLE_DEAD:
[[[BDBInvalidDatabaseHandle alloc]
initWithErrorCode: (int) status] raise];
case EINVAL:
[[[ECIllegalArgumentException alloc]
initWithArgumentInfo:
@"BDBDatabase::appendEntryWithTransaction: Unable to append the "\
"specified database entry. Check the size of the record. Possibly"\
" an attempt was made to append data to a secondary index." ]
raise];
case ENOSPC:
[[[BDBBtreeMaximumDepthReachedException alloc]
initWithErrorCode: (int) status] raise];
default: /* ok */ break;
}
}
EC_AUTORELEASEPOOL_END
return status;
}
- close {
if( NULL != db ) {
db->sync( db, 0 );
db->close( db, 0 );
db = NULL;
[ databaseName release ];
databaseName = nil;
}
return self;
}
- (BDBOperationStatus) deleteEntryWithTransaction:( BDBTransaction *) transaction
key: (BDBDatabaseEntryData *) key {
BDBOperationStatus status = BDB_STATUS_UNKNOWN;
DBT dbKey;
NSAssert( nil == transaction, @"Transaction not supported at present!" );
memset( &dbKey, 0, sizeof( dbKey ) );
dbKey.data = (void *) [[key data] bytes];
dbKey.size = [[key data] length];
status = db->del( db, NULL, &dbKey, 0 );
BDB_ERROR_CODE_EVAL_BEGIN(status);
BDB_ERROR_CODE_EVAL_END;
return status;
}
- (BDBOperationStatus) getEntryWithTransaction: (BDBTransaction *) transaction
key: (BDBDatabaseEntryData *) key data: (BDBDatabaseEntryData *) data
flags: (u_int32_t) flags {
NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];
NSData *valueData;
DBT dbKey, dbValue;
int dbResult = BDB_STATUS_UNKNOWN;
// todo
NSAssert( nil == transaction, @"Transaction not supported at present!" );
if( nil == key || nil == data ) {
[[[ECIllegalArgumentException alloc ]
initWithArgumentInfo: @"key or data equals nil"] raise];
}
memset( &dbKey, 0, sizeof( dbKey ) );
memset( &dbValue, 0, sizeof( dbValue ) );
dbKey.data = (void *) [ [ key data] bytes ];
dbKey.size = [ [ key data ] length ];
dbValue.flags = DB_DBT_MALLOC;
dbResult = db->get(db, NULL, &dbKey, &dbValue, flags);
BDB_ERROR_CODE_EVAL_BEGIN(dbResult);
BDB_ERROR_CODE_EVAL_ON_THROW_BEGIN
[ pool release ];
if( NULL != dbValue.data ) {
free( dbValue.data );
}
BDB_ERROR_CODE_EVAL_ON_THROW_END
BDB_ERROR_CODE_EVAL_END;
if( BDB_STATUS_SUCCESS == dbResult ) {
/* at present we do not support user buffers, so :*/
valueData = [ [ [ NSData alloc ] initWithBytes:dbValue.data
length:dbValue.size ] autorelease ] ;
[ data setData: valueData ];
}
if( NULL != dbValue.data ) {
free( dbValue.data );
}
[ pool release ];
return dbResult;
}
- (BDBOperationStatus) getEntryWithTransaction: (BDBTransaction *) transaction
key: (BDBDatabaseEntryData *) key data: (BDBDatabaseEntryData *) data {
return [self getEntryWithTransaction: transaction key:key
data: data flags: 0];
}
- (BDBOperationStatus) getEntryWithTransaction: (BDBTransaction *) transaction
recordNumber: (BDBDatabaseRecordNumber *) recNo
data: (BDBDatabaseEntryData *) data {
u_int32_t flags;
if( BDB_BTREE == [self->dbConfig databaseType] ) {
flags = DB_SET_RECNO;
} else {
flags = 0;
}
return [ self getEntryWithTransaction: transaction key:recNo data: data
flags: flags ];
}
- (BDBCursor *) openCursor: (BDBTransaction *) transaction
cursorConfig: (BDBCursorConfig *) config {
DBC *cursor;
BDBOperationStatus result;
BDBCursor *toReturn = nil;
// todo
NSAssert( nil == transaction, @"Transaction not supported at present!" );
result = db->cursor( db, NULL, &cursor, [config flags] );
BDB_ERROR_CODE_EVAL_BEGIN(result);
BDB_ERROR_CODE_EVAL_ON_THROW_BEGIN
BDB_ERROR_CODE_EVAL_ON_THROW_END
BDB_ERROR_CODE_EVAL_END;
NSAssert( BDB_STATUS_SUCCESS == result, @"Got unknown error code" );
toReturn = [[BDBCursor alloc] initWithCursor: cursor config: config ];
return [toReturn autorelease];
}
- (BDBOperationStatus) putEntryWithTransaction: (BDBTransaction *) transaction
key: (BDBDatabaseEntryData *) aKey value: (BDBDatabaseEntryData *) value {
NSData *keyData, *valueData;
BDBOperationStatus toReturn = BDB_STATUS_UNKNOWN;
// todo
NSAssert( nil == transaction, @"Transaction not supported at present!" );
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
keyData = [ aKey data ];
valueData = [ value data ];
toReturn = [ self putDataWithTransaction: transaction
key: keyData value:valueData ];
[ pool release ];
return toReturn;
}
- (BDBOperationStatus) putDataWithTransaction: (BDBTransaction *) transaction
key: (NSData *) key value: (NSData *) value {
DBT dbKey, dbValue;
BDBOperationStatus result = BDB_STATUS_UNKNOWN;
// todo
NSAssert( nil == transaction, @"Transaction not supported at present!" );
memset( &dbKey, 0, sizeof( DBT ) );
memset( &dbValue, 0, sizeof( DBT ) );
dbKey.data = (void *) ([ key bytes ]);
dbKey.size = [ key length ];
dbValue.data = (void *) ([ value bytes ]);
dbValue.size = [ value length ];
result = db->put( db, NULL, &dbKey, &dbValue, 0 );
BDB_ERROR_CODE_EVAL_BEGIN(result);
BDB_ERROR_CODE_EVAL_END;
if( BDB_STATUS_SUCCESS != result ) {
[[[BDBException alloc ] initWithErrorCode: result] raise];
}
return result;
}
@end