Adding support for list database directories and txn'lly deleting databases

This commit is contained in:
Dave Smith 2008-12-17 13:07:36 -07:00
parent f9b3354c50
commit 68813d0a53
6 changed files with 142 additions and 41 deletions

View file

@ -21,6 +21,7 @@
*/
static int open_database(const char* name, DBTYPE type, unsigned flags, PortData* data, int* errno);
static int close_database(int dbref, unsigned flags, PortData* data);
static int delete_database(const char* name);
static void tune_system(int target, void* values, BinHelper* bh);
@ -325,7 +326,6 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
char** outbuf, int outbuf_sz)
{
PortData* d = (PortData*)handle;
switch(cmd)
{
case CMD_OPEN_DB:
@ -484,11 +484,9 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
int target = UNPACK_INT(inbuf, 0);
char* values = UNPACK_BLOB(inbuf, 4);
// Setup a binhelper
BinHelper bh;
// Execute the tuning -- the result to send back to the caller is wrapped
// up in the provided binhelper
BinHelper bh;
tune_system(target, values, &bh);
RETURN_BH(bh, outbuf);
}
@ -562,6 +560,21 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
RETURN_INT(0, outbuf);
}
case CMD_REMOVE_DB:
{
FAIL_IF_ASYNC_PENDING(d, outbuf);
// Fail if a txn is open
if (d->txn != 0)
{
RETURN_INT(ERROR_TXN_OPEN, outbuf);
}
// Inbuf is: << dbname/bytes, 0:8 >>
const char* dbname = UNPACK_STRING(inbuf, 0);
int rc = delete_database(dbname);
RETURN_INT(rc, outbuf);
}
}
*outbuf = 0;
return 0;
@ -722,6 +735,24 @@ static int close_database(int dbref, unsigned flags, PortData* data)
}
}
static int delete_database(const char* name)
{
// Go directly to a write lock on the global databases structure
WRITE_LOCK(G_DATABASES_RWLOCK);
// Make sure the database is not opened by anyone
if (hive_hash_get(G_DATABASES_NAMES, name))
{
WRITE_UNLOCK(G_DATABASES_RWLOCK);
return ERROR_DB_ACTIVE;
}
// Good, database doesn't seem to be open -- attempt the delete
int rc = G_DB_ENV->dbremove(G_DB_ENV, 0, name, 0, DB_AUTO_COMMIT);
WRITE_UNLOCK(G_DATABASES_RWLOCK);
return rc;
}
/**
* Given a target system parameter/action adjust/return the requested value
*/
@ -729,16 +760,6 @@ static void tune_system(int target, void* values, BinHelper* bh)
{
switch(target)
{
case SYSP_CACHESIZE_SET:
{
unsigned int gbytes = UNPACK_INT(values, 0);
unsigned int bytes = UNPACK_INT(values, 4);
unsigned int ncache = UNPACK_INT(values, 8);
int rc = G_DB_ENV->set_cachesize(G_DB_ENV, gbytes, bytes, ncache);
bin_helper_init(bh, 4);
bin_helper_push_int32(bh, rc);
break;
}
case SYSP_CACHESIZE_GET:
{
unsigned int gbytes = 0;
@ -769,6 +790,23 @@ static void tune_system(int target, void* values, BinHelper* bh)
bin_helper_push_int32(bh, timeout);
break;
}
case SYSP_DATA_DIR_GET:
{
const char** dirs = 0;
int rc = G_DB_ENV->get_data_dirs(G_DB_ENV, &dirs);
printf("DATA DIR: %d\n", rc);
bin_helper_init(bh, 64);
bin_helper_push_int32(bh, rc);
if (dirs)
{
while (*dirs != 0)
{
bin_helper_push_string(bh, *dirs);
dirs++;
}
}
break;
}
}
}

View file

@ -43,6 +43,7 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
#define CMD_CURSOR_PREV 12
#define CMD_CURSOR_CLOSE 13
#define CMD_PUT_COMMIT 14
#define CMD_REMOVE_DB 15
/**
@ -68,19 +69,16 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
#define ERROR_NO_TXN (-29004) /* No transaction open on this port */
#define ERROR_CURSOR_OPEN (-29005) /* Cursor already active on this port */
#define ERROR_NO_CURSOR (-29006) /* No cursor open on this port */
#define ERROR_DB_ACTIVE (-29007) /* Database is currently active; operation requires otherwise */
/**
* Tunable system parameters/actions
*/
#define SYSP_CACHESIZE_SET 0
#define SYSP_CACHESIZE_GET 1
#define SYSP_TXN_TIMEOUT_SET 2
#define SYSP_TXN_TIMEOUT_GET 3
#define SYSP_TXN_TIMEOUT 1
#define SYSP_DEADLOCK_CHECK_INTERVAL 2
#define SYSP_TRICKLE_INTERVAL 3
#define SYSP_TRICKLE_PERCENTAGE 4
#define SYSP_DATA_DIR_GET 4
/**
* Driver Entry

View file

@ -14,8 +14,7 @@ static void bin_helper_check_size(BinHelper* bh, int space_needed)
{
if (bh->offset + space_needed > bh->bin->orig_size)
{
// Need to realloc space -- grow by 2 * space_needed
bh->bin = driver_realloc_binary(bh->bin, bh->bin->orig_size + (2 * space_needed));
bh->bin = driver_realloc_binary(bh->bin, bh->offset + space_needed);
}
}

View file

@ -20,15 +20,15 @@
-define(CMD_CURSOR_PREV, 12).
-define(CMD_CURSOR_CLOSE, 13).
-define(CMD_PUT_COMMIT, 14).
-define(CMD_REMOVE_DB, 15).
-define(DB_TYPE_BTREE, 1).
-define(DB_TYPE_HASH, 2).
-define(SYSP_CACHESIZE_SET, 0).
-define(SYSP_CACHESIZE_GET, 1).
-define(SYSP_TXN_TIMEOUT_SET, 2).
-define(SYSP_TXN_TIMEOUT_GET, 3).
-define(SYSP_DATA_DIR_GET, 4).
-define(STATUS_OK, 0).
-define(STATUS_ERROR, 1).
@ -41,6 +41,7 @@
-define(ERROR_NO_TXN, -29004). % No transaction open on this port
-define(ERROR_CURSOR_OPEN, -29005). % Cursor already active on this port
-define(ERROR_NO_CURSOR, -29006). % No cursor open on this port
-define(ERROR_DB_ACTIVE, -29007). % Database is currently active; operation requires otherwise
-define(ERROR_DB_LOCK_NOTGRANTED, -30993). % Lock was busy and not granted
-define(ERROR_DB_LOCK_DEADLOCK, -30994). % Deadlock occurred

View file

@ -10,7 +10,7 @@
close/1, close/2,
txn_begin/0, txn_begin/1,
txn_commit/0, txn_commit/1, txn_abort/0,
get_cache_size/0, set_cache_size/3,
get_cache_size/0, get_data_dirs/0,
get_txn_timeout/0, set_txn_timeout/1,
transaction/1, transaction/2,
put/3, put/4,
@ -20,6 +20,7 @@
get/2, get/3,
get_r/2, get_r/3,
update/3, update/4,
delete_database/1,
cursor_open/1, cursor_next/0, cursor_prev/0, cursor_current/0, cursor_close/0]).
-include("bdberl.hrl").
@ -236,7 +237,35 @@ cursor_close() ->
Reason ->
{error, Reason}
end.
delete_database(Filename) ->
Cmd = list_to_binary(Filename),
<<Rc:32/native-signed>> = erlang:port_control(get_port(), ?CMD_REMOVE_DB, Cmd),
case decode_rc(Rc) of
ok ->
ok;
Reason ->
{error, Reason}
end.
get_data_dirs() ->
%% Call into the BDB library and get a list of configured data directories
Cmd = <<?SYSP_DATA_DIR_GET:32/native>>,
<<Result:32/signed-native, Rest/bytes>> = erlang:port_control(get_port(), ?CMD_TUNE, Cmd),
case decode_rc(Result) of
ok ->
Dirs = [binary_to_list(D) || D <- split_bin(0, Rest, <<>>, [])],
%% Make sure DB_HOME is part of the list
case lists:member(os:getenv("DB_HOME"), Dirs) of
true ->
Dirs;
false ->
[os:getenv("DB_HOME") | Dirs]
end;
Reason ->
{error, Reason}
end.
get_cache_size() ->
Cmd = <<?SYSP_CACHESIZE_GET:32/native>>,
@ -249,17 +278,6 @@ get_cache_size() ->
{error, Result}
end.
set_cache_size(Gbytes, Bytes, Ncaches) ->
Cmd = <<?SYSP_CACHESIZE_SET:32/native, Gbytes:32/native, Bytes:32/native, Ncaches:32/native>>,
<<Result:32/signed-native>> = erlang:port_control(get_port(), ?CMD_TUNE, Cmd),
case Result of
0 ->
ok;
_ ->
{error, Result}
end.
get_txn_timeout() ->
Cmd = <<?SYSP_TXN_TIMEOUT_GET:32/native>>,
<<Result:32/signed-native, Timeout:32/native>> = erlang:port_control(get_port(), ?CMD_TUNE, Cmd),
@ -281,6 +299,7 @@ set_txn_timeout(Timeout) ->
end.
%% ====================================================================
%% Internal functions
%% ====================================================================
@ -404,3 +423,17 @@ do_cursor_move(Direction) ->
Reason ->
{error, Reason}
end.
%%
%% Split a binary into pieces, using a single character delimiter
%%
split_bin(_Delimiter, <<>>, <<>>, Acc) ->
lists:reverse(Acc);
split_bin(_Delimiter, <<>>, ItemAcc, Acc) ->
lists:reverse([ItemAcc | Acc]);
split_bin(Delimiter, <<Delimiter:8, Rest/binary>>, ItemAcc, Acc) ->
split_bin(Delimiter, Rest, <<>> ,[ItemAcc | Acc]);
split_bin(Delimiter, <<Other:8, Rest/binary>>, ItemAcc, Acc) ->
split_bin(Delimiter, Rest, <<ItemAcc/binary, Other:8>>, Acc).

View file

@ -10,7 +10,7 @@
-include_lib("ct.hrl").
all() ->
ball() ->
[open_should_create_database_if_none_exists,
open_should_allow_opening_multiple_databases,
close_should_fail_with_invalid_db_handle,
@ -25,20 +25,33 @@ all() ->
update_should_accept_args_for_fun,
port_should_tune_transaction_timeouts,
cursor_should_iterate, cursor_should_fail_if_not_open,
put_commit_should_end_txn].
put_commit_should_end_txn,
data_dir_should_be_priv_dir,
delete_should_remove_file,
delete_should_fail_if_db_inuse].
init_per_suite(Config) ->
DbHome = ?config(priv_dir, Config),
os:putenv("DB_HOME", DbHome),
ok = file:write_file(DbHome ++ "DB_CONFIG", <<"set_data_dir ", (list_to_binary(DbHome))/binary, "\n">>),
Config.
end_per_suite(_Config) ->
ok.
init_per_testcase(_TestCase, Config) ->
{ok, Db} = bdberl:open("api_test.db", btree, [create, exclusive]),
[{db, Db}|Config].
end_per_testcase(_TestCase, Config) ->
ok = bdberl:close(?config(db, Config)),
ok = file:delete("api_test.db").
ok = file:delete(filename:join([?config(priv_dir, Config), "api_test.db"])).
open_should_create_database_if_none_exists(_Config) ->
true = filelib:is_file("api_test.db").
open_should_create_database_if_none_exists(Config) ->
DbName = filename:join([?config(priv_dir, Config), "api_test.db"]),
true = filelib:is_file(DbName).
open_should_allow_opening_multiple_databases(_Config) ->
%% Open up another db -- should use dbref 1 as that's the first available
@ -171,4 +184,23 @@ put_commit_should_end_txn(Config) ->
%% Verify data got committed
{ok, value1} = bdberl:get(Db, key1).
data_dir_should_be_priv_dir(Config) ->
PrivDir = ?config(priv_dir, Config),
[PrivDir] = bdberl:get_data_dirs().
delete_should_remove_file(Config) ->
{ok, Db} = bdberl:open("mytest.bdb", btree),
ok = bdberl:close(Db),
Fname = filename:join([?config(priv_dir, Config), "mytest.bdb"]),
true = filelib:is_file(Fname),
ok = bdberl:delete_database("mytest.bdb"),
false = filelib:is_file(Fname).
delete_should_fail_if_db_inuse(Config) ->
Fname = filename:join([?config(priv_dir, Config), "api_test.db"]),
true = filelib:is_file(Fname),
{error, _} = bdberl:delete_database(Fname),
true = filelib:is_file(Fname).