diff --git a/c_src/wterl.c b/c_src/wterl.c index 2e052e9..397b8d9 100644 --- a/c_src/wterl.c +++ b/c_src/wterl.c @@ -46,7 +46,6 @@ typedef struct WT_CURSOR* cursor; } wterl_cursor_handle; -typedef char Config[256]; // configuration strings typedef char Uri[128]; // object names // Atoms (initialized in on_load) @@ -115,7 +114,7 @@ static ErlNifFunc nif_funcs[] = static inline ERL_NIF_TERM wterl_strerror(ErlNifEnv* env, int rc) { if (rc == WT_NOTFOUND) - return enif_make_tuple2(env, ATOM_ERROR, enif_make_atom(env, "not_found")); + return enif_make_atom(env, "not_found"); else return enif_make_tuple2(env, ATOM_ERROR, enif_make_string(env, wiredtiger_strerror(rc), ERL_NIF_LATIN1)); @@ -123,13 +122,13 @@ static inline ERL_NIF_TERM wterl_strerror(ErlNifEnv* env, int rc) static ERL_NIF_TERM wterl_conn_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - Config config; + ErlNifBinary config; char homedir[4096]; if (enif_get_string(env, argv[0], homedir, sizeof homedir, ERL_NIF_LATIN1) && - enif_get_string(env, argv[1], config, sizeof config, ERL_NIF_LATIN1)) + enif_inspect_binary(env, argv[1], &config)) { WT_CONNECTION* conn; - int rc = wiredtiger_open(homedir, NULL, config, &conn); + int rc = wiredtiger_open(homedir, NULL, (const char*)config.data, &conn); if (rc == 0) { wterl_conn_handle* conn_handle = @@ -179,36 +178,36 @@ static inline ERL_NIF_TERM wterl_session_worker(ErlNifEnv* env, int argc, const if (enif_get_resource(env, argv[0], wterl_session_RESOURCE, (void**)&session_handle)) { WT_SESSION* session = session_handle->session; - Config config; + ErlNifBinary config; Uri uri; if (enif_get_string(env, argv[1], uri, sizeof uri, ERL_NIF_LATIN1) && - enif_get_string(env, argv[2], config, sizeof config, ERL_NIF_LATIN1)) + enif_inspect_binary(env, argv[2], &config)) { int rc; switch (op) { case WTERL_OP_CREATE: - rc = session->create(session, uri, config); + rc = session->create(session, uri, (const char*)config.data); break; case WTERL_OP_DROP: - rc = session->drop(session, uri, config); + rc = session->drop(session, uri, (const char*)config.data); break; case WTERL_OP_SALVAGE: - rc = session->salvage(session, uri, config); + rc = session->salvage(session, uri, (const char*)config.data); break; case WTERL_OP_SYNC: - rc = session->sync(session, uri, config); + rc = session->sync(session, uri, (const char*)config.data); break; case WTERL_OP_TRUNCATE: // Ignore the cursor start/stop form of truncation for now, // support only the full file truncation. - rc = session->truncate(session, uri, NULL, NULL, config); + rc = session->truncate(session, uri, NULL, NULL, (const char*)config.data); break; case WTERL_OP_UPGRADE: - rc = session->upgrade(session, uri, config); + rc = session->upgrade(session, uri, (const char*)config.data); break; default: case WTERL_OP_VERIFY: - rc = session->verify(session, uri, config); + rc = session->verify(session, uri, (const char*)config.data); break; } if (rc != 0) @@ -285,13 +284,13 @@ static ERL_NIF_TERM wterl_session_rename(ErlNifEnv* env, int argc, const ERL_NIF if (enif_get_resource(env, argv[0], wterl_session_RESOURCE, (void**)&session_handle)) { WT_SESSION* session = session_handle->session; - Config config; + ErlNifBinary config; Uri oldname, newname; if (enif_get_string(env, argv[1], oldname, sizeof oldname, ERL_NIF_LATIN1) && enif_get_string(env, argv[2], newname, sizeof newname, ERL_NIF_LATIN1) && - enif_get_string(env, argv[3], config, sizeof config, ERL_NIF_LATIN1)) + enif_inspect_binary(env, argv[3], &config)) { - int rc = session->rename(session, oldname, newname, config); + int rc = session->rename(session, oldname, newname, (const char*)config.data); if (rc != 0) { return wterl_strerror(env, rc); diff --git a/src/wterl.app.src b/src/wterl.app.src index 7a4bc5c..534c8f0 100644 --- a/src/wterl.app.src +++ b/src/wterl.app.src @@ -7,6 +7,7 @@ kernel, stdlib ]}, + {mod, {wterl_app, []}}, {env, [ ]} ]}. diff --git a/src/wterl.erl b/src/wterl.erl index 37c6282..7d26d13 100644 --- a/src/wterl.erl +++ b/src/wterl.erl @@ -55,7 +55,7 @@ session_upgrade/3, session_verify/2, session_verify/3, - config_to_bin/2]). + config_to_bin/1]). -on_load(init/0). @@ -63,6 +63,8 @@ nif_stub_error(Line) -> erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}). +-define(EMPTY_CONFIG, <<"\0">>). + -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. @@ -91,12 +93,12 @@ session_close(_Ref) -> ?nif_stub. session_create(Ref, Name) -> - session_create(Ref, Name, ""). + session_create(Ref, Name, ?EMPTY_CONFIG). session_create(_Ref, _Name, _Config) -> ?nif_stub. session_drop(Ref, Name) -> - session_drop(Ref, Name, ""). + session_drop(Ref, Name, ?EMPTY_CONFIG). session_drop(_Ref, _Name, _Config) -> ?nif_stub. @@ -110,32 +112,32 @@ session_put(_Ref, _Table, _Key, _Value) -> ?nif_stub. session_rename(Ref, OldName, NewName) -> - session_rename(Ref, OldName, NewName, ""). + session_rename(Ref, OldName, NewName, ?EMPTY_CONFIG). session_rename(_Ref, _OldName, _NewName, _Config) -> ?nif_stub. session_salvage(Ref, Name) -> - session_salvage(Ref, Name, ""). + session_salvage(Ref, Name, ?EMPTY_CONFIG). session_salvage(_Ref, _Name, _Config) -> ?nif_stub. session_sync(Ref, Name) -> - session_sync(Ref, Name, ""). + session_sync(Ref, Name, ?EMPTY_CONFIG). session_sync(_Ref, _Name, _Config) -> ?nif_stub. session_truncate(Ref, Name) -> - session_truncate(Ref, Name, ""). + session_truncate(Ref, Name, ?EMPTY_CONFIG). session_truncate(_Ref, _Name, _Config) -> ?nif_stub. session_upgrade(Ref, Name) -> - session_upgrade(Ref, Name, ""). + session_upgrade(Ref, Name, ?EMPTY_CONFIG). session_upgrade(_Ref, _Name, _Config) -> ?nif_stub. session_verify(Ref, Name) -> - session_verify(Ref, Name, ""). + session_verify(Ref, Name, ?EMPTY_CONFIG). session_verify(_Ref, _Name, _Config) -> ?nif_stub. @@ -179,6 +181,7 @@ config_types() -> {eviction_target, integer}, {eviction_trigger, integer}, {extensions, string}, + {force, bool}, {hazard_max, integer}, {home_environment, bool}, {home_environment_priv, bool}, @@ -203,6 +206,8 @@ config_encode(bool, false) -> config_encode(_Type, _Value) -> invalid. +config_to_bin(Opts) -> + config_to_bin(Opts, []). config_to_bin([], Acc) -> iolist_to_binary([Acc, <<"\0">>]); config_to_bin([{Key, Value} | Rest], Acc) -> @@ -215,7 +220,7 @@ config_to_bin([{Key, Value} | Rest], Acc) -> Acc; EncodedValue -> EncodedKey = atom_to_binary(Key, utf8), - [EncodedKey, <<" = ">>, EncodedValue, <<", ">> | Acc] + [EncodedKey, <<"=">>, EncodedValue, <<",">> | Acc] end, config_to_bin(Rest, Acc2); false -> @@ -235,13 +240,13 @@ basic_test() -> ok = filelib:ensure_dir(filename:join("/tmp/wterl.basic", "foo")), io:put_chars(standard_error, "\topen connection\n"), - {ok, ConnRef} = - conn_open("/tmp/wterl.basic", "create=true,cache_size=100MB"), + OpenConfig = config_to_bin([{create,true},{cache_size,"100MB"}]), + {ok, ConnRef} = conn_open("/tmp/wterl.basic", OpenConfig), {ok, SRef} = session_open(ConnRef), %% Remove the table from any earlier run and re-create it io:put_chars(standard_error, "\ttable drop/create\n"), - ok = session_drop(SRef, "table:test", "force"), + ok = session_drop(SRef, "table:test", config_to_bin([{force,true}])), ok = session_create(SRef, "table:test"), %% Insert/delete a key using the session handle @@ -249,7 +254,7 @@ basic_test() -> ok = session_put(SRef, "table:test", <<"a">>, <<"apple">>), {ok, <<"apple">>} = session_get(SRef, "table:test", <<"a">>), ok = session_delete(SRef, "table:test", <<"a">>), - {error, not_found} = session_get(SRef, "table:test", <<"a">>), + not_found = session_get(SRef, "table:test", <<"a">>), %% Insert some items io:put_chars(standard_error, "\tsession insert\n"), @@ -277,7 +282,7 @@ basic_test() -> io:put_chars(standard_error, "\tsession truncate\n"), ok = session_truncate(SRef, "table:test"), - {error, not_found} = session_get(SRef, "table:test", <<"a">>), + not_found = session_get(SRef, "table:test", <<"a">>), ok = session_put(SRef, "table:test", <<"a">>, <<"apple">>), ok = session_put(SRef, "table:test", <<"b">>, <<"banana">>), @@ -314,7 +319,7 @@ basic_test() -> {ok, <<"cherry">>} = cursor_prev(Cursor), {ok, <<"date">>} = cursor_next(Cursor), {ok, <<"gooseberry">>} = cursor_next(Cursor), - {error, not_found} = cursor_next(Cursor), + not_found = cursor_next(Cursor), ok = cursor_close(Cursor), %% Search for an item @@ -352,7 +357,7 @@ basic_test() -> io:put_chars(standard_error, "\tcursor update\n"), {ok, Cursor} = cursor_open(SRef, "table:test"), ok = cursor_update(Cursor, <<"g">>, <<"goji berries">>), - {error, not_found} = cursor_update(Cursor, <<"k">>, <<"kumquat">>), + not_found = cursor_update(Cursor, <<"k">>, <<"kumquat">>), ok = cursor_close(Cursor), {ok, <<"goji berries">>} = session_get(SRef, "table:test", <<"g">>), @@ -360,9 +365,9 @@ basic_test() -> io:put_chars(standard_error, "\tcursor remove\n"), {ok, Cursor} = cursor_open(SRef, "table:test"), ok = cursor_remove(Cursor, <<"g">>, <<"goji berries">>), - {error, not_found} = cursor_remove(Cursor, <<"l">>, <<"lemon">>), + not_found = cursor_remove(Cursor, <<"l">>, <<"lemon">>), ok = cursor_close(Cursor), - {error, not_found} = session_get(SRef, "table:test", <<"g">>), + not_found = session_get(SRef, "table:test", <<"g">>), %% Close the session/connection io:put_chars(standard_error, "\tsession/connection close\n"), diff --git a/src/wterl_app.erl b/src/wterl_app.erl new file mode 100644 index 0000000..2b34fbc --- /dev/null +++ b/src/wterl_app.erl @@ -0,0 +1,39 @@ +%% ------------------------------------------------------------------- +%% +%% wterl: access to WiredTiger database +%% +%% Copyright (c) 2012 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- +-module(wterl_app). +-author('Steve Vinoski '). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + + +%% =================================================================== +%% Application callbacks +%% =================================================================== + +start(_StartType, _StartArgs) -> + wterl_sup:start_link(). + +stop(_State) -> + ok. diff --git a/src/wterl_conn.erl b/src/wterl_conn.erl new file mode 100644 index 0000000..1b03942 --- /dev/null +++ b/src/wterl_conn.erl @@ -0,0 +1,111 @@ +%% ------------------------------------------------------------------- +%% +%% wterl_conn: manage a connection to WiredTiger +%% +%% Copyright (c) 2012 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- +-module(wterl_conn). +-author('Steve Vinoski '). + +-behaviour(gen_server). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). +-endif. + +%% API +-export([start_link/0, + open/1, get/0, close/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-record(state, { conn }). + +%% ==================================================================== +%% API +%% ==================================================================== + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +open(Dir) -> + gen_server:call(?MODULE, {open, Dir}, infinity). + +get() -> + gen_server:call(?MODULE, get, infinity). + +close() -> + gen_server:call(?MODULE, close, infinity). + +%% ==================================================================== +%% gen_server callbacks +%% ==================================================================== + +init([]) -> + {ok, #state{}}. + +handle_call({open, Dir}, _From, #state{conn = undefined}=State) -> + Opts = [{create, true}, {cache_size, "100MB"}], + {Reply, NState} = case wterl:conn_open(Dir, wterl:config_to_bin(Opts)) of + {ok, ConnRef}=OK -> + {OK, State#state{conn = ConnRef}}; + Error -> + {Error, State} + end, + {reply, Reply, NState}; +handle_call({open, _Dir}, _From,#state{conn = ConnRef}=State) -> + {reply, {ok, ConnRef}, State}; + +handle_call(get, _From, #state{conn = undefined}=State) -> + {reply, {error, "no connection"}, State}; +handle_call(get, _From, #state{conn = ConnRef}=State) -> + {reply, {ok, ConnRef}, State}; + +handle_call(close, _From, #state{conn = ConnRef}=State) -> + close(ConnRef), + {reply, ok, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, #state{conn = ConnRef}) -> + close(ConnRef), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% ==================================================================== +%% Internal functions +%% ==================================================================== + +close(undefined) -> + ok; +close(ConnRef) -> + wterl:conn_close(ConnRef). + + +-ifdef(TEST). + + +-endif. diff --git a/src/wterl_sup.erl b/src/wterl_sup.erl new file mode 100644 index 0000000..0ead62a --- /dev/null +++ b/src/wterl_sup.erl @@ -0,0 +1,48 @@ +%% ------------------------------------------------------------------- +%% +%% wterl_sup: supervisor for WiredTiger database app +%% +%% Copyright (c) 2012 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- +-module(wterl_sup). +-author('Steve Vinoski '). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% Helper macro for declaring children of supervisor +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). + +%% =================================================================== +%% API functions +%% =================================================================== +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== + +init([]) -> + {ok, {{one_for_one, 5, 10}, [?CHILD(wterl_conn, worker)]}}.