summaryrefslogtreecommitdiff
path: root/src/couch_replicator/src/couch_replicator_filters.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch_replicator/src/couch_replicator_filters.erl')
-rw-r--r--src/couch_replicator/src/couch_replicator_filters.erl129
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.