/*! @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