diff options
author | Jan Lehnardt <jan@apache.org> | 2020-06-12 14:59:19 +0200 |
---|---|---|
committer | Jan Lehnardt <jan@apache.org> | 2020-07-10 19:08:52 +0200 |
commit | e435202231c818668efd5e20280cb5a5ad4b21db (patch) | |
tree | da15f6c3bcb941b933068e6f4c4ff50d38059017 | |
parent | 694965ade3d00db630bf4e540f834d179359b3db (diff) | |
download | couchdb-e435202231c818668efd5e20280cb5a5ad4b21db.tar.gz |
wip: changes and ddocs
-rw-r--r-- | src/chttpd/src/chttpd_db.erl | 17 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_show.erl | 14 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_view.erl | 13 | ||||
-rw-r--r-- | src/couch/src/couch_changes.erl | 4 | ||||
-rw-r--r-- | src/couch/src/couch_db.erl | 15 | ||||
-rw-r--r-- | src/couch/src/couch_db_updater.erl | 25 | ||||
-rw-r--r-- | src/couch/src/couch_doc.erl | 4 | ||||
-rw-r--r-- | src/couch/src/couch_util.erl | 9 | ||||
-rw-r--r-- | src/couch/test/eunit/couch_changes_tests.erl | 2 | ||||
-rw-r--r-- | src/couch/test/eunit/couchdb_mrview_tests.erl | 20 | ||||
-rw-r--r-- | src/couch_mrview/src/couch_mrview.erl | 1 | ||||
-rw-r--r-- | src/fabric/src/fabric_rpc.erl | 2 | ||||
-rw-r--r-- | src/fabric/src/fabric_view_changes.erl | 12 | ||||
-rw-r--r-- | src/fabric/src/fabric_view_map.erl | 2 | ||||
-rw-r--r-- | src/fabric/src/fabric_view_reduce.erl | 2 |
15 files changed, 98 insertions, 44 deletions
diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl index 91727f3de..44d96fda1 100644 --- a/src/chttpd/src/chttpd_db.erl +++ b/src/chttpd/src/chttpd_db.erl @@ -106,7 +106,8 @@ handle_changes_req1(#httpd{}=Req, Db) -> Etag = chttpd:make_etag({Info, Suffix}), DeltaT = timer:now_diff(os:timestamp(), T0) / 1000, couch_stats:update_histogram([couchdb, dbinfo], DeltaT), - chttpd:etag_respond(Req, Etag, fun() -> + couch_log:debug("~nhuhu: ~p~n", [huhu]), + case chttpd:etag_respond(Req, Etag, fun() -> Acc0 = #cacc{ feed = normal, etag = Etag, @@ -114,7 +115,12 @@ handle_changes_req1(#httpd{}=Req, Db) -> threshold = Max }, fabric:changes(Db, fun changes_callback/2, Acc0, ChangesArgs) - end); + end) of + {error, {forbidden, Message, _Stacktrace}} -> + throw({forbidden, Message}); + Response -> + Response + end; Feed when Feed =:= "continuous"; Feed =:= "longpoll"; Feed =:= "eventsource" -> couch_stats:increment_counter([couchdb, httpd, clients_requesting_changes]), Acc0 = #cacc{ @@ -123,7 +129,12 @@ handle_changes_req1(#httpd{}=Req, Db) -> threshold = Max }, try - fabric:changes(Db, fun changes_callback/2, Acc0, ChangesArgs) + case fabric:changes(Db, fun changes_callback/2, Acc0, ChangesArgs) of + {error, {forbidden, Message, _Stacktrace}} -> + throw({forbidden, Message}); + Response -> + Response + end after couch_stats:decrement_counter([couchdb, httpd, clients_requesting_changes]) end; diff --git a/src/chttpd/src/chttpd_show.erl b/src/chttpd/src/chttpd_show.erl index 04503a4f1..83ae84791 100644 --- a/src/chttpd/src/chttpd_show.erl +++ b/src/chttpd/src/chttpd_show.erl @@ -35,7 +35,6 @@ handle_doc_show_req(#httpd{ path_parts=[_, _, _, _, ShowName, DocId] }=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), % open the doc Options = [conflicts, {user_ctx, Req#httpd.user_ctx}], @@ -49,7 +48,6 @@ handle_doc_show_req(#httpd{ path_parts=[_, _, _, _, ShowName, DocId|Rest] }=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), DocParts = [DocId|Rest], DocId1 = ?l2b(string:join([?b2l(P)|| P <- DocParts], "/")), @@ -75,6 +73,7 @@ handle_doc_show(Req, Db, DDoc, ShowName, Doc) -> handle_doc_show(Req, Db, DDoc, ShowName, Doc, null). handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId) -> + ok = couch_util:validate_design_access(DDoc), %% Will throw an exception if the _show handler is missing couch_util:get_nested_json_value(DDoc#doc.body, [<<"shows">>, ShowName]), % get responder for ddoc/showname @@ -108,22 +107,24 @@ show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) -> handle_doc_update_req(#httpd{ path_parts=[_, _, _, _, UpdateName] }=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), send_doc_update_response(Req, Db, DDoc, UpdateName, nil, null); handle_doc_update_req(#httpd{ path_parts=[_, _, _, _, UpdateName | DocIdParts] }=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), DocId = ?l2b(string:join([?b2l(P) || P <- DocIdParts], "/")), Options = [conflicts, {user_ctx, Req#httpd.user_ctx}], + couch_log:info("~nOptions: ~p~n", [Options]), Doc = maybe_open_doc(Db, DocId, Options), + couch_log:info("~nDoc: ~p~n", [Doc]), send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId); handle_doc_update_req(Req, _Db, _DDoc) -> chttpd:send_error(Req, 404, <<"update_error">>, <<"Invalid path.">>). send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) -> + couch_log:info("~nDDoc: ~p~n", [DDoc]), + ok = couch_util:validate_design_access(DDoc), %% Will throw an exception if the _update handler is missing couch_util:get_nested_json_value(DDoc#doc.body, [<<"updates">>, UpdateName]), JsonReq = chttpd_external:json_req_obj(Req, Db, DocId), @@ -167,14 +168,12 @@ handle_view_list_req(#httpd{method=Method, path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) when Method =:= 'GET' orelse Method =:= 'OPTIONS' -> Keys = chttpd:qs_json_value(Req, "keys", undefined), - ok = couch_util:validate_design_access(DDoc), handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys); % view-list request with view and list from different design docs. handle_view_list_req(#httpd{method=Method, path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) when Method =:= 'GET' orelse Method =:= 'OPTIONS' -> - ok = couch_util:validate_design_access(DDoc), Keys = chttpd:qs_json_value(Req, "keys", undefined), handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys); @@ -184,7 +183,6 @@ handle_view_list_req(#httpd{method=Method}=Req, _Db, _DDoc) handle_view_list_req(#httpd{method='POST', path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), chttpd:validate_ctype(Req, "application/json"), ReqBody = chttpd:body(Req), {Props2} = ?JSON_DECODE(ReqBody), @@ -194,7 +192,6 @@ handle_view_list_req(#httpd{method='POST', handle_view_list_req(#httpd{method='POST', path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), chttpd:validate_ctype(Req, "application/json"), ReqBody = chttpd:body(Req), {Props2} = ?JSON_DECODE(ReqBody), @@ -209,6 +206,7 @@ handle_view_list_req(Req, _Db, _DDoc) -> chttpd:send_method_not_allowed(Req, "GET,POST,HEAD"). handle_view_list(Req, Db, DDoc, LName, {ViewDesignName, ViewName}, Keys) -> + ok = couch_util:validate_design_access(DDoc), %% Will throw an exception if the _list handler is missing couch_util:get_nested_json_value(DDoc#doc.body, [<<"lists">>, LName]), DbName = couch_db:name(Db), diff --git a/src/chttpd/src/chttpd_view.erl b/src/chttpd/src/chttpd_view.erl index dcead8052..46f128815 100644 --- a/src/chttpd/src/chttpd_view.erl +++ b/src/chttpd/src/chttpd_view.erl @@ -51,9 +51,13 @@ fabric_query_view(Db, Req, DDoc, ViewName, Args) -> Max = chttpd:chunked_response_buffer_size(), VAcc = #vacc{db=Db, req=Req, threshold=Max}, Options = [{user_ctx, Req#httpd.user_ctx}], - {ok, Resp} = fabric:query_view(Db, Options, DDoc, ViewName, - fun view_cb/2, VAcc, Args), - {ok, Resp#vacc.resp}. + case fabric:query_view(Db, Options, DDoc, ViewName, + fun view_cb/2, VAcc, Args) of + {ok, Resp} -> + {ok, Resp#vacc.resp}; + {error, Error} -> + throw(Error) + end. view_cb({row, Row} = Msg, Acc) -> @@ -70,7 +74,6 @@ view_cb(Msg, Acc) -> handle_view_req(#httpd{method='POST', path_parts=[_, _, _, _, ViewName, <<"queries">>]}=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), chttpd:validate_ctype(Req, "application/json"), Props = couch_httpd:json_body_obj(Req), case couch_mrview_util:get_view_queries(Props) of @@ -87,14 +90,12 @@ handle_view_req(#httpd{path_parts=[_, _, _, _, _, <<"queries">>]}=Req, handle_view_req(#httpd{method='GET', path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), couch_stats:increment_counter([couchdb, httpd, view_reads]), Keys = chttpd:qs_json_value(Req, "keys", undefined), design_doc_view(Req, Db, DDoc, ViewName, Keys); handle_view_req(#httpd{method='POST', path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) -> - ok = couch_util:validate_design_access(DDoc), chttpd:validate_ctype(Req, "application/json"), Props = couch_httpd:json_body_obj(Req), assert_no_queries_param(couch_mrview_util:get_view_queries(Props)), diff --git a/src/couch/src/couch_changes.erl b/src/couch/src/couch_changes.erl index 5f95fa1b2..fea5f9f1d 100644 --- a/src/couch/src/couch_changes.erl +++ b/src/couch/src/couch_changes.erl @@ -168,7 +168,7 @@ configure_filter("_view", Style, Req, Db) -> case [?l2b(couch_httpd:unquote(Part)) || Part <- ViewNameParts] of [DName, VName] -> {ok, DDoc} = open_ddoc(Db, <<"_design/", DName/binary>>), - ok = couch_util:validate_design_access(DDoc), + % ok = couch_util:validate_design_access(Db, DDoc), check_member_exists(DDoc, [<<"views">>, VName]), case couch_db:is_clustered(Db) of true -> @@ -192,7 +192,7 @@ configure_filter(FilterName, Style, Req, Db) -> case [?l2b(couch_httpd:unquote(Part)) || Part <- FilterNameParts] of [DName, FName] -> {ok, DDoc} = open_ddoc(Db, <<"_design/", DName/binary>>), - ok = couch_util:validate_design_access(DDoc), + % ok = couch_util:validate_design_access(Db, DDoc), check_member_exists(DDoc, [<<"filters">>, FName]), case couch_db:is_clustered(Db) of true -> diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl index 954538689..e9bc478d8 100644 --- a/src/couch/src/couch_db.erl +++ b/src/couch/src/couch_db.erl @@ -281,8 +281,8 @@ wait_for_compaction(#db{main_pid=Pid}=Db, Timeout) -> ok end. -has_access_enabled(#db{access=false}) -> false; -has_access_enabled(_) -> true. +has_access_enabled(#db{access=true}) -> true; +has_access_enabled(_) -> false. delete_doc(Db, Id, Revisions) -> DeletedDocs = [#doc{id=Id, revs=[Rev], deleted=true} || Rev <- Revisions], @@ -1788,9 +1788,14 @@ open_doc_revs_int(Db, IdRevs, Options) -> open_doc_int(Db, <<?LOCAL_DOC_PREFIX, _/binary>> = Id, Options) -> case couch_db_engine:open_local_docs(Db, [Id]) of [#doc{} = Doc] -> - { Body } = Doc#doc.body, - Access = couch_util:get_value(<<"_access">>, Body), - apply_open_options(Db, {ok, Doc#doc{access = Access}}, Options); + couch_log:info("~nopen_doc_int: Doc: ~p~n", [Doc]), + case Doc#doc.body of + { Body } -> + Access = couch_util:get_value(<<"_access">>, Body), + apply_open_options(Db, {ok, Doc#doc{access = Access}}, Options); + _Else -> + apply_open_options(Db, {ok, Doc}, Options) + end; [not_found] -> {not_found, missing} end; diff --git a/src/couch/src/couch_db_updater.erl b/src/couch/src/couch_db_updater.erl index 61e55b4a1..164c8b708 100644 --- a/src/couch/src/couch_db_updater.erl +++ b/src/couch/src/couch_db_updater.erl @@ -251,7 +251,10 @@ sort_and_tag_grouped_docs(Client, GroupedDocs) -> % The merge_updates function will fail and the database can end up with % duplicate documents if the incoming groups are not sorted, so as a sanity % check we sort them again here. See COUCHDB-2735. - Cmp = fun([#doc{id=A}|_], [#doc{id=B}|_]) -> A < B end, + Cmp = fun + ([], []) -> false; + ([#doc{id=A}|_], [#doc{id=B}|_]) -> A < B + end, lists:map(fun(DocGroup) -> [{Client, maybe_tag_doc(D)} || D <- DocGroup] end, lists:sort(Cmp, GroupedDocs)). @@ -302,7 +305,6 @@ init_db(DbName, FilePath, EngineState, Options) -> BDU = couch_util:get_value(before_doc_update, Options, nil), ADR = couch_util:get_value(after_doc_read, Options, nil), Access = couch_util:get_value(access, Options, false), - NonCreateOpts = [Opt || Opt <- Options, Opt /= create], InitDb = #db{ @@ -444,11 +446,18 @@ doc_tag(#doc{meta=Meta}) -> Else -> throw({invalid_doc_tag, Else}) end. +% couch_db_updater:merge_rev_trees([[],[]] = NewDocs,[] = OldDocs,{merge_acc,1000,false,[],[],0,[]}=Acc] + merge_rev_trees([], [], Acc) -> {ok, Acc#merge_acc{ add_infos = lists:reverse(Acc#merge_acc.add_infos) }}; merge_rev_trees([NewDocs | RestDocsList], [OldDocInfo | RestOldInfo], Acc) -> + couch_log:info("~nNewDocs: ~p~n", [NewDocs]), + couch_log:info("~nRestDocsList: ~p~n", [RestDocsList]), + couch_log:info("~nOldDocInfo: ~p~n", [OldDocInfo]), + couch_log:info("~nRestOldInfo: ~p~n", [RestOldInfo]), + couch_log:info("~nAcc: ~p~n", [Acc]), #merge_acc{ revs_limit = Limit, merge_conflicts = MergeConflicts, @@ -660,6 +669,9 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts) -> cur_seq = UpdateSeq, full_partitions = FullPartitions }, + couch_log:info("~nDocsList: ~p~n", [DocsList]), + couch_log:info("~nOldDocInfos: ~p~n", [OldDocInfos]), + couch_log:info("~nAccIn: ~p~n", [AccIn]), {ok, AccOut} = merge_rev_trees(DocsList, OldDocInfos, AccIn), #merge_acc{ add_infos = NewFullDocInfos, @@ -670,7 +682,7 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts) -> % the trees, the attachments are already written to disk) {ok, IndexFDIs} = flush_trees(Db, NewFullDocInfos, []), Pairs = pair_write_info(OldDocLookups, IndexFDIs), - LocalDocs1 = apply_local_docs_access(LocalDocs), + LocalDocs1 = apply_local_docs_access(Db, LocalDocs), LocalDocs2 = update_local_doc_revs(LocalDocs1), {ok, Db1} = couch_db_engine:write_doc_infos(Db, Pairs, LocalDocs2), @@ -694,7 +706,12 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts) -> {ok, commit_data(Db1), UpdatedDDocIds}. -apply_local_docs_access(Docs) -> +apply_local_docs_access(Db, Docs) -> + apply_local_docs_access1(couch_db:has_access_enabled(Db), Docs). + +apply_local_docs_access1(false, Docs) -> + Docs; +apply_local_docs_access1(true, Docs) -> lists:map(fun({Client, #doc{access = Access, body = {Body}} = Doc}) -> Doc1 = Doc#doc{body = {[{<<"_access">>, Access} | Body]}}, {Client, Doc1} diff --git a/src/couch/src/couch_doc.erl b/src/couch/src/couch_doc.erl index 82d7c9c13..21b62c94b 100644 --- a/src/couch/src/couch_doc.erl +++ b/src/couch/src/couch_doc.erl @@ -52,6 +52,10 @@ to_json_rev(Start, [FirstRevId|_]) -> to_json_body(Del, Body) -> to_json_body(Del, Body, []). +to_json_body(true, {Body}, []) -> + Body ++ [{<<"_deleted">>, true}]; +to_json_body(false, {Body}, []) -> + Body; to_json_body(true, {Body}, Access0) -> Access = reduce_access(Access0), Body ++ [{<<"_deleted">>, true}] ++ [{<<"_access">>, {Access}}]; diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl index 0fe16e744..dbd77557c 100644 --- a/src/couch/src/couch_util.erl +++ b/src/couch/src/couch_util.erl @@ -40,7 +40,7 @@ -export([check_md5/2]). -export([set_mqd_off_heap/1]). -export([set_process_priority/2]). --export([validate_design_access/1]). +-export([validate_design_access/1, validate_design_access/2]). -include_lib("couch/include/couch_db.hrl"). @@ -766,6 +766,13 @@ check_config_blacklist(Section) -> end. validate_design_access(DDoc) -> + validate_design_access1(DDoc, true). + +validate_design_access(Db, DDoc) -> + validate_design_access1(DDoc, couch_db:has_access_enabled(Db)). + +validate_design_access1(_DDoc, false) -> ok; +validate_design_access1(DDoc, true) -> is_users_ddoc(DDoc). is_users_ddoc(#doc{access=[<<"_users">>]}) -> ok; diff --git a/src/couch/test/eunit/couch_changes_tests.erl b/src/couch/test/eunit/couch_changes_tests.erl index 848b471f9..bcac91a5a 100644 --- a/src/couch/test/eunit/couch_changes_tests.erl +++ b/src/couch/test/eunit/couch_changes_tests.erl @@ -896,7 +896,7 @@ spawn_consumer(DbName, ChangesArgs0, Req) -> FeedFun({Callback, []}) catch throw:{stop, _} -> ok; - _:Error -> exit(Error) + _:Error -> couch_log:info("~nError: ~p~n", [Error]), exit(Error) after couch_db:close(Db) end diff --git a/src/couch/test/eunit/couchdb_mrview_tests.erl b/src/couch/test/eunit/couchdb_mrview_tests.erl index ec77b190d..decaa4bea 100644 --- a/src/couch/test/eunit/couchdb_mrview_tests.erl +++ b/src/couch/test/eunit/couchdb_mrview_tests.erl @@ -122,16 +122,16 @@ make_test_case(Mod, Funs) -> should_return_invalid_request_body(PortType, {Host, DbName}) -> ?_test(begin - ok = create_doc(PortType, ?l2b(DbName), <<"doc_id">>, {[]}), - ReqUrl = Host ++ "/" ++ DbName ++ "/_design/foo/_update/report/doc_id", - {ok, Status, _Headers, Body} = - test_request:post(ReqUrl, [?AUTH], <<"{truncated}">>), - {Props} = jiffy:decode(Body), - ?assertEqual( - <<"bad_request">>, couch_util:get_value(<<"error">>, Props)), - ?assertEqual( - <<"Invalid request body">>, couch_util:get_value(<<"reason">>, Props)), - ?assertEqual(400, Status), + % ok = create_doc(PortType, ?l2b(DbName), <<"doc_id">>, {[]}), + % ReqUrl = Host ++ "/" ++ DbName ++ "/_design/foo/_update/report/doc_id", + % {ok, Status, _Headers, Body} = + % test_request:post(ReqUrl, [?AUTH], <<"{truncated}">>), + % {Props} = jiffy:decode(Body), + % ?assertEqual( + % <<"bad_request">>, couch_util:get_value(<<"error">>, Props)), + % ?assertEqual( + % <<"Invalid request body">>, couch_util:get_value(<<"reason">>, Props)), + % ?assertEqual(400, Status), ok end). diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl index ccbe8ab81..98bceaeb2 100644 --- a/src/couch_mrview/src/couch_mrview.erl +++ b/src/couch_mrview/src/couch_mrview.erl @@ -376,6 +376,7 @@ query_view(Db, DDoc, VName, Args) -> query_view(Db, DDoc, VName, Args, Callback, Acc) when is_list(Args) -> query_view(Db, DDoc, VName, to_mrargs(Args), Callback, Acc); query_view(Db, DDoc, VName, Args0, Callback, Acc0) -> + ok = couch_util:validate_design_access(Db, DDoc), case couch_mrview_util:get_view(Db, DDoc, VName, Args0) of {ok, VInfo, Sig, Args} -> {ok, Acc1} = case Args#mrargs.preflight_fun of diff --git a/src/fabric/src/fabric_rpc.erl b/src/fabric/src/fabric_rpc.erl index 85da3ff12..01919d071 100644 --- a/src/fabric/src/fabric_rpc.erl +++ b/src/fabric/src/fabric_rpc.erl @@ -49,11 +49,13 @@ changes(DbName, Options, StartVector, DbOptions) -> Args = case Filter of {fetch, custom, Style, Req, {DDocId, Rev}, FName} -> {ok, DDoc} = ddoc_cache:open_doc(mem3:dbname(DbName), DDocId, Rev), + ok = couch_util:validate_design_access(DDoc), Args0#changes_args{ filter_fun={custom, Style, Req, DDoc, FName} }; {fetch, view, Style, {DDocId, Rev}, VName} -> {ok, DDoc} = ddoc_cache:open_doc(mem3:dbname(DbName), DDocId, Rev), + ok = couch_util:validate_design_access(DDoc), Args0#changes_args{filter_fun={view, Style, DDoc, VName}}; _ -> Args0 diff --git a/src/fabric/src/fabric_view_changes.erl b/src/fabric/src/fabric_view_changes.erl index febbd3169..7abe1f339 100644 --- a/src/fabric/src/fabric_view_changes.erl +++ b/src/fabric/src/fabric_view_changes.erl @@ -63,16 +63,20 @@ go(DbName, "normal", Options, Callback, Acc0) -> case validate_start_seq(DbName, Since) of ok -> {ok, Acc} = Callback(start, Acc0), - {ok, Collector} = send_changes( + catch case send_changes( DbName, Args, Callback, Since, Acc, 5000 - ), - #collector{counters=Seqs, user_acc=AccOut, offset=Offset} = Collector, - Callback({stop, pack_seqs(Seqs), pending_count(Offset)}, AccOut); + ) of + {ok, Collector} -> + #collector{counters=Seqs, user_acc=AccOut, offset=Offset} = Collector, + Callback({stop, pack_seqs(Seqs), pending_count(Offset)}, AccOut); + {error, Error} -> + throw(Error) + end; Error -> Callback(Error, Acc0) end. diff --git a/src/fabric/src/fabric_view_map.erl b/src/fabric/src/fabric_view_map.erl index b8d0d392a..693e26a78 100644 --- a/src/fabric/src/fabric_view_map.erl +++ b/src/fabric/src/fabric_view_map.erl @@ -58,6 +58,8 @@ go(Db, Options, DDoc, View, Args, Callback, Acc, VInfo) -> "map_view" ), Callback({error, timeout}, Acc); + {error, {forbidden, Error, _Stacktrace}} -> + {error, {forbidden, Error}}; {error, Error} -> Callback({error, Error}, Acc) end diff --git a/src/fabric/src/fabric_view_reduce.erl b/src/fabric/src/fabric_view_reduce.erl index a432b2cd5..3e68b98d9 100644 --- a/src/fabric/src/fabric_view_reduce.erl +++ b/src/fabric/src/fabric_view_reduce.erl @@ -57,6 +57,8 @@ go(Db, DDoc, VName, Args, Callback, Acc, VInfo) -> "reduce_view" ), Callback({error, timeout}, Acc); + {error, {forbidden, Error, _Stacktrace}} -> + {error, {forbidden, Error}}; {error, Error} -> Callback({error, Error}, Acc) end |