diff options
Diffstat (limited to 'src/couch_replicator/src/couch_replicator_filters.erl')
-rw-r--r-- | src/couch_replicator/src/couch_replicator_filters.erl | 129 |
1 files changed, 68 insertions, 61 deletions
diff --git a/src/couch_replicator/src/couch_replicator_filters.erl b/src/couch_replicator/src/couch_replicator_filters.erl index c8980001a..aab8e80b3 100644 --- a/src/couch_replicator/src/couch_replicator_filters.erl +++ b/src/couch_replicator/src/couch_replicator_filters.erl @@ -21,19 +21,18 @@ -include_lib("couch/include/couch_db.hrl"). - % Parse the filter from replication options proplist. % Return {ok, {FilterType,...}} | {error, ParseError}. % For `user` filter, i.e. filters specified as user code % in source database, this code doesn't fetch the filter % code, but only returns the name of the filter. -spec parse([_]) -> - {ok, nil} | - {ok, {view, binary(), {[_]}}} | - {ok, {user, {binary(), binary()}, {[_]}}} | - {ok, {docids, [_]}} | - {ok, {mango, {[_]}}} | - {error, binary()}. + {ok, nil} + | {ok, {view, binary(), {[_]}}} + | {ok, {user, {binary(), binary()}, {[_]}}} + | {ok, {docids, [_]}} + | {ok, {mango, {[_]}}} + | {error, binary()}. parse(Options) -> Filter = couch_util:get_value(filter, Options), DocIds = couch_util:get_value(doc_ids, Options), @@ -59,7 +58,6 @@ parse(Options) -> {error, list_to_binary(Err)} end. - % Fetches body of filter function from source database. Guaranteed to either % return {ok, Body} or an {error, Reason}. Also assume this function might % block due to network / socket issues for an undeterminted amount of time. @@ -86,18 +84,17 @@ fetch(DDocName, FilterName, Source) -> {error, couch_util:to_binary(Reason)} end. - % Get replication type and view (if any) from replication document props -spec view_type([_], [_]) -> {view, {binary(), binary()}} | {db, nil} | {error, binary()}. view_type(Props, Options) -> case couch_util:get_value(<<"filter">>, Props) of <<"_view">> -> - {QP} = couch_util:get_value(query_params, Options, {[]}), + {QP} = couch_util:get_value(query_params, Options, {[]}), ViewParam = couch_util:get_value(<<"view">>, QP), case re:split(ViewParam, <<"/">>) of [DName, ViewName] -> - {view, {<< "_design/", DName/binary >>, ViewName}}; + {view, {<<"_design/", DName/binary>>, ViewName}}; _ -> {error, <<"Invalid `view` parameter.">>} end; @@ -105,57 +102,70 @@ view_type(Props, Options) -> {db, nil} end. - % Private functions fetch_internal(DDocName, FilterName, Source) -> - Db = case (catch couch_replicator_api_wrap:db_open(Source)) of - {ok, Db0} -> - Db0; - DbError -> - DbErrorMsg = io_lib:format("Could not open source database `~s`: ~s", - [couch_replicator_api_wrap:db_uri(Source), - couch_util:to_binary(DbError)]), - throw({fetch_error, iolist_to_binary(DbErrorMsg)}) - end, - try - Body = case (catch couch_replicator_api_wrap:open_doc( - Db, <<"_design/", DDocName/binary>>, [ejson_body])) of - {ok, #doc{body = Body0}} -> - Body0; - DocError -> - DocErrorMsg = io_lib:format( - "Couldn't open document `_design/~s` from source " - "database `~s`: ~s", [DDocName, - couch_replicator_api_wrap:db_uri(Source), - couch_util:to_binary(DocError)] - ), - throw({fetch_error, iolist_to_binary(DocErrorMsg)}) + Db = + case (catch couch_replicator_api_wrap:db_open(Source)) of + {ok, Db0} -> + Db0; + DbError -> + DbErrorMsg = io_lib:format( + "Could not open source database `~s`: ~s", + [ + couch_replicator_api_wrap:db_uri(Source), + couch_util:to_binary(DbError) + ] + ), + throw({fetch_error, iolist_to_binary(DbErrorMsg)}) end, + try + Body = + case + (catch couch_replicator_api_wrap:open_doc( + Db, <<"_design/", DDocName/binary>>, [ejson_body] + )) + of + {ok, #doc{body = Body0}} -> + Body0; + DocError -> + DocErrorMsg = io_lib:format( + "Couldn't open document `_design/~s` from source " + "database `~s`: ~s", + [ + DDocName, + couch_replicator_api_wrap:db_uri(Source), + couch_util:to_binary(DocError) + ] + ), + throw({fetch_error, iolist_to_binary(DocErrorMsg)}) + end, try Code = couch_util:get_nested_json_value( - Body, [<<"filters">>, FilterName]), + Body, [<<"filters">>, FilterName] + ), re:replace(Code, [$^, "\s*(.*?)\s*", $$], "\\1", [{return, binary}]) - catch - _Tag:CodeError -> - CodeErrorMsg = io_lib:format( - "Couldn't parse filter code from document ~s on `~s` " - " Error: ~s", [DDocName, - couch_replicator_api_wrap:db_uri(Source), - couch_util:to_binary(CodeError)] - ), - throw({fetch_error, CodeErrorMsg}) - end + catch + _Tag:CodeError -> + CodeErrorMsg = io_lib:format( + "Couldn't parse filter code from document ~s on `~s` " + " Error: ~s", + [ + DDocName, + couch_replicator_api_wrap:db_uri(Source), + couch_util:to_binary(CodeError) + ] + ), + throw({fetch_error, CodeErrorMsg}) + end after couch_replicator_api_wrap:db_close(Db) end. - -spec query_params([_]) -> {[_]}. -query_params(Options)-> +query_params(Options) -> couch_util:get_value(query_params, Options, {[]}). - parse_user_filter(Filter) -> case re:run(Filter, "(.*?)/(.*)", [{capture, [1, 2], binary}]) of {match, [DDocName0, FilterName0]} -> @@ -164,31 +174,27 @@ parse_user_filter(Filter) -> {error, <<"Invalid filter. Must match `ddocname/filtername`.">>} end. - % Sort an EJSON object's properties to attempt % to generate a unique representation. This is used % to reduce the chance of getting different % replication checkpoints for the same Mango selector -ejsort({V})-> +ejsort({V}) -> ejsort_props(V, []); ejsort(V) when is_list(V) -> ejsort_array(V, []); ejsort(V) -> V. - -ejsort_props([], Acc)-> +ejsort_props([], Acc) -> {lists:keysort(1, Acc)}; -ejsort_props([{K, V}| R], Acc) -> +ejsort_props([{K, V} | R], Acc) -> ejsort_props(R, [{K, ejsort(V)} | Acc]). - -ejsort_array([], Acc)-> +ejsort_array([], Acc) -> lists:reverse(Acc); ejsort_array([V | R], Acc) -> ejsort_array(R, [ejsort(V) | Acc]). - -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). @@ -200,14 +206,15 @@ ejsort_basic_values_test() -> ?assertEqual(ejsort([]), []), ?assertEqual(ejsort({[]}), {[]}). - ejsort_compound_values_test() -> ?assertEqual(ejsort([2, 1, 3, <<"a">>]), [2, 1, 3, <<"a">>]), - Ej1 = {[{<<"a">>, 0}, {<<"c">>, 0}, {<<"b">>, 0}]}, - Ej1s = {[{<<"a">>, 0}, {<<"b">>, 0}, {<<"c">>, 0}]}, + Ej1 = {[{<<"a">>, 0}, {<<"c">>, 0}, {<<"b">>, 0}]}, + Ej1s = {[{<<"a">>, 0}, {<<"b">>, 0}, {<<"c">>, 0}]}, ?assertEqual(ejsort(Ej1), Ej1s), Ej2 = {[{<<"x">>, Ej1}, {<<"z">>, Ej1}, {<<"y">>, [Ej1, Ej1]}]}, - ?assertEqual(ejsort(Ej2), - {[{<<"x">>, Ej1s}, {<<"y">>, [Ej1s, Ej1s]}, {<<"z">>, Ej1s}]}). + ?assertEqual( + ejsort(Ej2), + {[{<<"x">>, Ej1s}, {<<"y">>, [Ej1s, Ej1s]}, {<<"z">>, Ej1s}]} + ). -endif. |