Adding support for list database directories and txn'lly deleting databases
This commit is contained in:
parent
f9b3354c50
commit
68813d0a53
|
@ -21,6 +21,7 @@
|
||||||
*/
|
*/
|
||||||
static int open_database(const char* name, DBTYPE type, unsigned flags, PortData* data, int* errno);
|
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 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);
|
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)
|
char** outbuf, int outbuf_sz)
|
||||||
{
|
{
|
||||||
PortData* d = (PortData*)handle;
|
PortData* d = (PortData*)handle;
|
||||||
|
|
||||||
switch(cmd)
|
switch(cmd)
|
||||||
{
|
{
|
||||||
case CMD_OPEN_DB:
|
case CMD_OPEN_DB:
|
||||||
|
@ -484,11 +484,9 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
|
||||||
int target = UNPACK_INT(inbuf, 0);
|
int target = UNPACK_INT(inbuf, 0);
|
||||||
char* values = UNPACK_BLOB(inbuf, 4);
|
char* values = UNPACK_BLOB(inbuf, 4);
|
||||||
|
|
||||||
// Setup a binhelper
|
|
||||||
BinHelper bh;
|
|
||||||
|
|
||||||
// Execute the tuning -- the result to send back to the caller is wrapped
|
// Execute the tuning -- the result to send back to the caller is wrapped
|
||||||
// up in the provided binhelper
|
// up in the provided binhelper
|
||||||
|
BinHelper bh;
|
||||||
tune_system(target, values, &bh);
|
tune_system(target, values, &bh);
|
||||||
RETURN_BH(bh, outbuf);
|
RETURN_BH(bh, outbuf);
|
||||||
}
|
}
|
||||||
|
@ -562,6 +560,21 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
|
||||||
|
|
||||||
RETURN_INT(0, outbuf);
|
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;
|
*outbuf = 0;
|
||||||
return 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
|
* 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)
|
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:
|
case SYSP_CACHESIZE_GET:
|
||||||
{
|
{
|
||||||
unsigned int gbytes = 0;
|
unsigned int gbytes = 0;
|
||||||
|
@ -769,6 +790,23 @@ static void tune_system(int target, void* values, BinHelper* bh)
|
||||||
bin_helper_push_int32(bh, timeout);
|
bin_helper_push_int32(bh, timeout);
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ static int bdberl_drv_control(ErlDrvData handle, unsigned int cmd,
|
||||||
#define CMD_CURSOR_PREV 12
|
#define CMD_CURSOR_PREV 12
|
||||||
#define CMD_CURSOR_CLOSE 13
|
#define CMD_CURSOR_CLOSE 13
|
||||||
#define CMD_PUT_COMMIT 14
|
#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_NO_TXN (-29004) /* No transaction open on this port */
|
||||||
#define ERROR_CURSOR_OPEN (-29005) /* Cursor already active 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_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
|
* Tunable system parameters/actions
|
||||||
*/
|
*/
|
||||||
#define SYSP_CACHESIZE_SET 0
|
|
||||||
#define SYSP_CACHESIZE_GET 1
|
#define SYSP_CACHESIZE_GET 1
|
||||||
#define SYSP_TXN_TIMEOUT_SET 2
|
#define SYSP_TXN_TIMEOUT_SET 2
|
||||||
#define SYSP_TXN_TIMEOUT_GET 3
|
#define SYSP_TXN_TIMEOUT_GET 3
|
||||||
#define SYSP_TXN_TIMEOUT 1
|
#define SYSP_DATA_DIR_GET 4
|
||||||
#define SYSP_DEADLOCK_CHECK_INTERVAL 2
|
|
||||||
#define SYSP_TRICKLE_INTERVAL 3
|
|
||||||
#define SYSP_TRICKLE_PERCENTAGE 4
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Driver Entry
|
* Driver Entry
|
||||||
|
|
|
@ -14,8 +14,7 @@ static void bin_helper_check_size(BinHelper* bh, int space_needed)
|
||||||
{
|
{
|
||||||
if (bh->offset + space_needed > bh->bin->orig_size)
|
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->offset + space_needed);
|
||||||
bh->bin = driver_realloc_binary(bh->bin, bh->bin->orig_size + (2 * space_needed));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,15 @@
|
||||||
-define(CMD_CURSOR_PREV, 12).
|
-define(CMD_CURSOR_PREV, 12).
|
||||||
-define(CMD_CURSOR_CLOSE, 13).
|
-define(CMD_CURSOR_CLOSE, 13).
|
||||||
-define(CMD_PUT_COMMIT, 14).
|
-define(CMD_PUT_COMMIT, 14).
|
||||||
|
-define(CMD_REMOVE_DB, 15).
|
||||||
|
|
||||||
-define(DB_TYPE_BTREE, 1).
|
-define(DB_TYPE_BTREE, 1).
|
||||||
-define(DB_TYPE_HASH, 2).
|
-define(DB_TYPE_HASH, 2).
|
||||||
|
|
||||||
-define(SYSP_CACHESIZE_SET, 0).
|
|
||||||
-define(SYSP_CACHESIZE_GET, 1).
|
-define(SYSP_CACHESIZE_GET, 1).
|
||||||
-define(SYSP_TXN_TIMEOUT_SET, 2).
|
-define(SYSP_TXN_TIMEOUT_SET, 2).
|
||||||
-define(SYSP_TXN_TIMEOUT_GET, 3).
|
-define(SYSP_TXN_TIMEOUT_GET, 3).
|
||||||
|
-define(SYSP_DATA_DIR_GET, 4).
|
||||||
|
|
||||||
-define(STATUS_OK, 0).
|
-define(STATUS_OK, 0).
|
||||||
-define(STATUS_ERROR, 1).
|
-define(STATUS_ERROR, 1).
|
||||||
|
@ -41,6 +41,7 @@
|
||||||
-define(ERROR_NO_TXN, -29004). % No transaction open on this port
|
-define(ERROR_NO_TXN, -29004). % No transaction open on this port
|
||||||
-define(ERROR_CURSOR_OPEN, -29005). % Cursor already active 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_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_NOTGRANTED, -30993). % Lock was busy and not granted
|
||||||
-define(ERROR_DB_LOCK_DEADLOCK, -30994). % Deadlock occurred
|
-define(ERROR_DB_LOCK_DEADLOCK, -30994). % Deadlock occurred
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
close/1, close/2,
|
close/1, close/2,
|
||||||
txn_begin/0, txn_begin/1,
|
txn_begin/0, txn_begin/1,
|
||||||
txn_commit/0, txn_commit/1, txn_abort/0,
|
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,
|
get_txn_timeout/0, set_txn_timeout/1,
|
||||||
transaction/1, transaction/2,
|
transaction/1, transaction/2,
|
||||||
put/3, put/4,
|
put/3, put/4,
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
get/2, get/3,
|
get/2, get/3,
|
||||||
get_r/2, get_r/3,
|
get_r/2, get_r/3,
|
||||||
update/3, update/4,
|
update/3, update/4,
|
||||||
|
delete_database/1,
|
||||||
cursor_open/1, cursor_next/0, cursor_prev/0, cursor_current/0, cursor_close/0]).
|
cursor_open/1, cursor_next/0, cursor_prev/0, cursor_current/0, cursor_close/0]).
|
||||||
|
|
||||||
-include("bdberl.hrl").
|
-include("bdberl.hrl").
|
||||||
|
@ -236,7 +237,35 @@ cursor_close() ->
|
||||||
Reason ->
|
Reason ->
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
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() ->
|
get_cache_size() ->
|
||||||
Cmd = <<?SYSP_CACHESIZE_GET:32/native>>,
|
Cmd = <<?SYSP_CACHESIZE_GET:32/native>>,
|
||||||
|
@ -249,17 +278,6 @@ get_cache_size() ->
|
||||||
{error, Result}
|
{error, Result}
|
||||||
end.
|
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() ->
|
get_txn_timeout() ->
|
||||||
Cmd = <<?SYSP_TXN_TIMEOUT_GET:32/native>>,
|
Cmd = <<?SYSP_TXN_TIMEOUT_GET:32/native>>,
|
||||||
<<Result:32/signed-native, Timeout:32/native>> = erlang:port_control(get_port(), ?CMD_TUNE, Cmd),
|
<<Result:32/signed-native, Timeout:32/native>> = erlang:port_control(get_port(), ?CMD_TUNE, Cmd),
|
||||||
|
@ -281,6 +299,7 @@ set_txn_timeout(Timeout) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%% ====================================================================
|
%% ====================================================================
|
||||||
|
@ -404,3 +423,17 @@ do_cursor_move(Direction) ->
|
||||||
Reason ->
|
Reason ->
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
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).
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
-include_lib("ct.hrl").
|
-include_lib("ct.hrl").
|
||||||
|
|
||||||
all() ->
|
ball() ->
|
||||||
[open_should_create_database_if_none_exists,
|
[open_should_create_database_if_none_exists,
|
||||||
open_should_allow_opening_multiple_databases,
|
open_should_allow_opening_multiple_databases,
|
||||||
close_should_fail_with_invalid_db_handle,
|
close_should_fail_with_invalid_db_handle,
|
||||||
|
@ -25,20 +25,33 @@ all() ->
|
||||||
update_should_accept_args_for_fun,
|
update_should_accept_args_for_fun,
|
||||||
port_should_tune_transaction_timeouts,
|
port_should_tune_transaction_timeouts,
|
||||||
cursor_should_iterate, cursor_should_fail_if_not_open,
|
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) ->
|
init_per_testcase(_TestCase, Config) ->
|
||||||
{ok, Db} = bdberl:open("api_test.db", btree, [create, exclusive]),
|
{ok, Db} = bdberl:open("api_test.db", btree, [create, exclusive]),
|
||||||
[{db, Db}|Config].
|
[{db, Db}|Config].
|
||||||
|
|
||||||
end_per_testcase(_TestCase, Config) ->
|
end_per_testcase(_TestCase, Config) ->
|
||||||
ok = bdberl:close(?config(db, 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) ->
|
open_should_create_database_if_none_exists(Config) ->
|
||||||
true = filelib:is_file("api_test.db").
|
DbName = filename:join([?config(priv_dir, Config), "api_test.db"]),
|
||||||
|
true = filelib:is_file(DbName).
|
||||||
|
|
||||||
open_should_allow_opening_multiple_databases(_Config) ->
|
open_should_allow_opening_multiple_databases(_Config) ->
|
||||||
%% Open up another db -- should use dbref 1 as that's the first available
|
%% 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
|
%% Verify data got committed
|
||||||
{ok, value1} = bdberl:get(Db, key1).
|
{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).
|
||||||
|
|
Loading…
Reference in a new issue