diff --git a/CHANGES b/CHANGES index d76407d..ae022ba 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,18 @@ Changelog of cowdb ------------------ +0.4.2 - 2014/01/04 + +- Internally uses the CBT library for all of our btree usages. +- fix: handle duplicate keys in transactions (#4) + +0.4.1 - 2014/08/17 + +- New license. CowDB is now released under the Mozilla Public License v2.0 +- cowdb now uses the C4.1 (Collective Code Construction Contract) process for +contributions. +- move code from bitbucket to github. + 0.4.0 - 2014/06/30 - rename cowdb:db_info/1 to cowdb:database_info/1 diff --git a/src/cowdb_updater.erl b/src/cowdb_updater.erl index 7b5bf22..07fd4f8 100644 --- a/src/cowdb_updater.erl +++ b/src/cowdb_updater.erl @@ -380,12 +380,21 @@ run_transaction([{add, Key, Value} | Rest], {ToAdd, ToRem}, Log, TransactId, %% only keep a reference so we don't have the value multiple time. {ok, Pos, Size} = cbt_file:append_term_crc32(Fd, Value), Value1 = {Key, {Pos, Size}, TransactId, Ts}, - run_transaction(Rest, {[{Key, Value1} | ToAdd], ToRem}, + %% make sure to replace duplicates + ToAdd2 = lists:keystore(Key, 1, ToAdd, {Key, Value1}), + run_transaction(Rest, {ToAdd2, ToRem}, [{add, Value1} | Log], TransactId, Ts, Db); -run_transaction([{remove, Key} | Rest], {ToAdd, ToRem}, Log, TransactId, Ts, Db) -> - %% remove a key - run_transaction(Rest, {ToAdd, [Key | ToRem]}, [{remove, Key} | Log], - TransactId, Ts, Db); +run_transaction([{remove, Key} | Rest], {ToAdd, ToRem}, Log, TransactId, Ts, + Db) -> + case lists:member(Key, ToRem) of + true -> + %% duplicate, continue + run_transaction(Rest, {ToAdd, ToRem}, Log, TransactId, Ts, Db); + false -> + %% remove a key + run_transaction(Rest, {ToAdd, [Key | ToRem]}, [{remove, Key} | Log], + TransactId, Ts, Db) + end; run_transaction([{fn, Func} | Rest], AddRemove, Log, TransactId, Ts, Db) -> %% execute a transaction function case cowdb_util:apply(Func, [Db]) of diff --git a/test/cowdb_basic_tests.erl b/test/cowdb_basic_tests.erl index d2df68a..e080aad 100644 --- a/test/cowdb_basic_tests.erl +++ b/test/cowdb_basic_tests.erl @@ -64,7 +64,9 @@ basic_ops_test_() -> fun should_fold_range/1, fun should_cancel_transact_fun/1, fun should_error_transact_fun/1, - fun shoudl_catch_transact_fun_error/1 + fun shoudl_catch_transact_fun_error/1, + fun should_handle_duplicate_kvs/1, + fun should_handle_delete_with_duplicate_keys/1 ]) }. @@ -250,3 +252,18 @@ shoudl_catch_transact_fun_error(Db) -> end, Reply = cowdb:transact(Db, [{fn, TransactFun}]), ?_assertMatch(badarg, Reply). + + +should_handle_duplicate_kvs(Db) -> + {ok, Tx} = cowdb:transact(Db, [{add, a, 1}, + {add, a, 3}]), + ?assertEqual(1, Tx), + ?_assertMatch([{ok, {a, 3}}], cowdb:mget(Db, [a])). + +should_handle_delete_with_duplicate_keys(Db) -> + {ok, 1} = cowdb:mput(Db, [{a, 1}, {b, 2}, {c, 3}]), + ?assertMatch([{ok, {a, 1}}, {ok, {b, 2}}, {ok, {c, 3}}], + cowdb:mget(Db, [a, b, c])), + {ok, 2} = cowdb:mdelete(Db, [a, a, b, c]), + ?_assertMatch([not_found, not_found, not_found], + cowdb:mget(Db, [a, b, c])).