diff options
Diffstat (limited to 'src/couchdb')
-rw-r--r-- | src/couchdb/couch_changes.erl | 32 | ||||
-rw-r--r-- | src/couchdb/couch_db.hrl | 2 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_db.erl | 19 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_show.erl | 19 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_view.erl | 54 |
5 files changed, 76 insertions, 50 deletions
diff --git a/src/couchdb/couch_changes.erl b/src/couchdb/couch_changes.erl index 6baaf7ec..2241e554 100644 --- a/src/couchdb/couch_changes.erl +++ b/src/couchdb/couch_changes.erl @@ -59,7 +59,7 @@ handle_changes(#changes_args{style=Style}=Args1, Req, Db) -> fun(CallbackAcc) -> {Callback, UserAcc} = get_callback_acc(CallbackAcc), UserAcc2 = start_sending_changes(Callback, UserAcc, Feed), - {ok, {_, LastSeq, _Prepend, _, _, UserAcc3, _, _, _}} = + {ok, {_, LastSeq, _Prepend, _, _, UserAcc3, _, _, _, _}} = send_changes( Args#changes_args{feed="normal"}, Callback, @@ -199,6 +199,7 @@ send_changes(Args, Callback, UserAcc, Db, StartSeq, Prepend) -> #changes_args{ style = Style, include_docs = IncludeDocs, + conflicts = Conflicts, limit = Limit, feed = ResponseType, dir = Dir, @@ -211,7 +212,7 @@ send_changes(Args, Callback, UserAcc, Db, StartSeq, Prepend) -> fun changes_enumerator/2, [{dir, Dir}], {Db, StartSeq, Prepend, FilterFun, Callback, UserAcc, ResponseType, - Limit, IncludeDocs} + Limit, IncludeDocs, Conflicts} ). keep_sending_changes(Args, Callback, UserAcc, Db, StartSeq, Prepend, Timeout, @@ -222,7 +223,7 @@ keep_sending_changes(Args, Callback, UserAcc, Db, StartSeq, Prepend, Timeout, db_open_options = DbOptions } = Args, % ?LOG_INFO("send_changes start ~p",[StartSeq]), - {ok, {_, EndSeq, Prepend2, _, _, UserAcc2, _, NewLimit, _}} = send_changes( + {ok, {_, EndSeq, Prepend2, _, _, UserAcc2, _, NewLimit, _, _}} = send_changes( Args#changes_args{dir=fwd}, Callback, UserAcc, Db, StartSeq, Prepend ), % ?LOG_INFO("send_changes last ~p",[EndSeq]), @@ -259,7 +260,7 @@ end_sending_changes(Callback, UserAcc, EndSeq, ResponseType) -> Callback({stop, EndSeq}, ResponseType, UserAcc). changes_enumerator(DocInfo, {Db, _, _, FilterFun, Callback, UserAcc, - "continuous", Limit, IncludeDocs}) -> + "continuous", Limit, IncludeDocs, Conflicts}) -> #doc_info{high_seq = Seq} = DocInfo, Results0 = FilterFun(DocInfo), @@ -268,17 +269,17 @@ changes_enumerator(DocInfo, {Db, _, _, FilterFun, Callback, UserAcc, case Results of [] -> {Go, {Db, Seq, nil, FilterFun, Callback, UserAcc, "continuous", Limit, - IncludeDocs} + IncludeDocs, Conflicts} }; _ -> - ChangesRow = changes_row(Db, Results, DocInfo, IncludeDocs), + ChangesRow = changes_row(Db, Results, DocInfo, IncludeDocs, Conflicts), UserAcc2 = Callback({change, ChangesRow, <<>>}, "continuous", UserAcc), {Go, {Db, Seq, nil, FilterFun, Callback, UserAcc2, "continuous", - Limit - 1, IncludeDocs} + Limit - 1, IncludeDocs, Conflicts} } end; changes_enumerator(DocInfo, {Db, _, Prepend, FilterFun, Callback, UserAcc, - ResponseType, Limit, IncludeDocs}) -> + ResponseType, Limit, IncludeDocs, Conflicts}) -> #doc_info{high_seq = Seq} = DocInfo, Results0 = FilterFun(DocInfo), @@ -287,25 +288,28 @@ changes_enumerator(DocInfo, {Db, _, Prepend, FilterFun, Callback, UserAcc, case Results of [] -> {Go, {Db, Seq, Prepend, FilterFun, Callback, UserAcc, ResponseType, - Limit, IncludeDocs} + Limit, IncludeDocs, Conflicts} }; _ -> - ChangesRow = changes_row(Db, Results, DocInfo, IncludeDocs), + ChangesRow = changes_row(Db, Results, DocInfo, IncludeDocs, Conflicts), UserAcc2 = Callback({change, ChangesRow, Prepend}, ResponseType, UserAcc), {Go, {Db, Seq, <<",\n">>, FilterFun, Callback, UserAcc2, ResponseType, - Limit - 1, IncludeDocs} + Limit - 1, IncludeDocs, Conflicts} } end. -changes_row(Db, Results, DocInfo, IncludeDoc) -> +changes_row(Db, Results, DocInfo, IncludeDoc, Conflicts) -> #doc_info{ id = Id, high_seq = Seq, revs = [#rev_info{deleted = Del} | _] } = DocInfo, {[{<<"seq">>, Seq}, {<<"id">>, Id}, {<<"changes">>, Results}] ++ deleted_item(Del) ++ case IncludeDoc of - true -> couch_httpd_view:doc_member(Db, DocInfo); - false -> [] + true -> + Options = if Conflicts -> [conflicts]; true -> [] end, + couch_httpd_view:doc_member(Db, DocInfo, Options); + false -> + [] end}. deleted_item(true) -> [{<<"deleted">>, true}]; diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl index 6b6c6440..003cb688 100644 --- a/src/couchdb/couch_db.hrl +++ b/src/couchdb/couch_db.hrl @@ -177,6 +177,7 @@ view_type = nil, include_docs = false, + conflicts = false, stale = false, multi_get = false, callback = nil, @@ -271,6 +272,7 @@ timeout, filter = "", include_docs = false, + conflicts = false, db_open_options = [] }). diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index 82e9b989..85c439ba 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -506,7 +506,7 @@ all_docs_view(Req, Db, Keys) -> FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, UpdateSeq, TotalRowCount, #view_fold_helper_funs{ reduce_count = fun couch_db:enum_docs_reduce_to_count/1, - send_row = fun all_docs_send_json_view_row/5 + send_row = fun all_docs_send_json_view_row/6 }), AdapterFun = fun(#full_doc_info{id=Id}=FullDocInfo, Offset, Acc) -> case couch_doc:to_doc_info(FullDocInfo) of @@ -524,7 +524,7 @@ all_docs_view(Req, Db, Keys) -> FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, UpdateSeq, TotalRowCount, #view_fold_helper_funs{ reduce_count = fun(Offset) -> Offset end, - send_row = fun all_docs_send_json_view_row/5 + send_row = fun all_docs_send_json_view_row/6 }), KeyFoldFun = case Dir of fwd -> @@ -551,21 +551,22 @@ all_docs_view(Req, Db, Keys) -> end end). -all_docs_send_json_view_row(Resp, Db, KV, IncludeDocs, RowFront) -> - JsonRow = all_docs_view_row_obj(Db, KV, IncludeDocs), +all_docs_send_json_view_row(Resp, Db, KV, IncludeDocs, Conflicts, RowFront) -> + JsonRow = all_docs_view_row_obj(Db, KV, IncludeDocs, Conflicts), send_chunk(Resp, RowFront ++ ?JSON_ENCODE(JsonRow)), {ok, ",\r\n"}. -all_docs_view_row_obj(_Db, {{DocId, error}, Value}, _IncludeDocs) -> +all_docs_view_row_obj(_Db, {{DocId, error}, Value}, _IncludeDocs, _Conflicts) -> {[{key, DocId}, {error, Value}]}; -all_docs_view_row_obj(Db, {_KeyDocId, DocInfo}, true) -> +all_docs_view_row_obj(Db, {_KeyDocId, DocInfo}, true, Conflicts) -> case DocInfo of #doc_info{revs = [#rev_info{deleted = true} | _]} -> {all_docs_row(DocInfo) ++ [{doc, null}]}; _ -> - {all_docs_row(DocInfo) ++ couch_httpd_view:doc_member(Db, DocInfo)} + {all_docs_row(DocInfo) ++ couch_httpd_view:doc_member( + Db, DocInfo, if Conflicts -> [conflicts]; true -> [] end)} end; -all_docs_view_row_obj(_Db, {_KeyDocId, DocInfo}, _IncludeDocs) -> +all_docs_view_row_obj(_Db, {_KeyDocId, DocInfo}, _IncludeDocs, _Conflicts) -> {all_docs_row(DocInfo)}. all_docs_row(#doc_info{id = Id, revs = [RevInfo | _]}) -> @@ -1221,6 +1222,8 @@ parse_changes_query(Req) -> Args#changes_args{timeout=list_to_integer(Value)}; {"include_docs", "true"} -> Args#changes_args{include_docs=true}; + {"conflicts", "true"} -> + Args#changes_args{conflicts=true}; {"filter", _} -> Args#changes_args{filter=Value}; _Else -> % unknown key value pair, ignore. diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl index 70dd82e1..59f74e1c 100644 --- a/src/couchdb/couch_httpd_show.erl +++ b/src/couchdb/couch_httpd_show.erl @@ -309,18 +309,20 @@ start_list_resp(QServer, LName, Req, Db, Head, Etag) -> {ok, Resp, ?b2l(?l2b(Chunks))}. make_map_send_row_fun(QueryServer) -> - fun(Resp, Db, Row, IncludeDocs, RowFront) -> - send_list_row(Resp, QueryServer, Db, Row, RowFront, IncludeDocs) + fun(Resp, Db, Row, IncludeDocs, Conflicts, RowFront) -> + send_list_row( + Resp, QueryServer, Db, Row, RowFront, IncludeDocs, Conflicts) end. make_reduce_send_row_fun(QueryServer, Db) -> fun(Resp, Row, RowFront) -> - send_list_row(Resp, QueryServer, Db, Row, RowFront, false) + send_list_row(Resp, QueryServer, Db, Row, RowFront, false, false) end. -send_list_row(Resp, QueryServer, Db, Row, RowFront, IncludeDoc) -> +send_list_row(Resp, QueryServer, Db, Row, RowFront, IncludeDoc, Conflicts) -> try - [Go,Chunks] = prompt_list_row(QueryServer, Db, Row, IncludeDoc), + [Go,Chunks] = prompt_list_row( + QueryServer, Db, Row, IncludeDoc, Conflicts), Chunk = RowFront ++ ?b2l(?l2b(Chunks)), send_non_empty_chunk(Resp, Chunk), case Go of @@ -336,11 +338,12 @@ send_list_row(Resp, QueryServer, Db, Row, RowFront, IncludeDoc) -> end. -prompt_list_row({Proc, _DDocId}, Db, {{Key, DocId}, Value}, IncludeDoc) -> - JsonRow = couch_httpd_view:view_row_obj(Db, {{Key, DocId}, Value}, IncludeDoc), +prompt_list_row({Proc, _DDocId}, Db, {{_Key, _DocId}, _} = Kv, + IncludeDoc, Conflicts) -> + JsonRow = couch_httpd_view:view_row_obj(Db, Kv, IncludeDoc, Conflicts), couch_query_servers:proc_prompt(Proc, [<<"list_row">>, JsonRow]); -prompt_list_row({Proc, _DDocId}, _, {Key, Value}, _IncludeDoc) -> +prompt_list_row({Proc, _DDocId}, _, {Key, Value}, _IncludeDoc, _Conflicts) -> JsonRow = {[{key, Key}, {value, Value}]}, couch_query_servers:proc_prompt(Proc, [<<"list_row">>, JsonRow]). diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl index 8906de70..b71fc2c6 100644 --- a/src/couchdb/couch_httpd_view.erl +++ b/src/couchdb/couch_httpd_view.erl @@ -16,9 +16,9 @@ -export([handle_view_req/3,handle_temp_view_req/2]). -export([parse_view_params/3]). --export([make_view_fold_fun/7, finish_view_fold/4, finish_view_fold/5, view_row_obj/3]). +-export([make_view_fold_fun/7, finish_view_fold/4, finish_view_fold/5, view_row_obj/4]). -export([view_etag/3, view_etag/4, make_reduce_fold_funs/6]). --export([design_doc_view/5, parse_bool_param/1, doc_member/2]). +-export([design_doc_view/5, parse_bool_param/1, doc_member/3]). -export([make_key_options/1, load_view/4]). -import(couch_httpd, @@ -319,6 +319,8 @@ parse_view_param("reduce", Value) -> [{reduce, parse_bool_param(Value)}]; parse_view_param("include_docs", Value) -> [{include_docs, parse_bool_param(Value)}]; +parse_view_param("conflicts", Value) -> + [{conflicts, parse_bool_param(Value)}]; parse_view_param("list", Value) -> [{list, ?l2b(Value)}]; parse_view_param("callback", _) -> @@ -427,6 +429,15 @@ validate_view_query(include_docs, true, Args) -> % Use the view_query_args record's default value validate_view_query(include_docs, _Value, Args) -> Args; +validate_view_query(conflicts, true, Args) -> + case Args#view_query_args.view_type of + reduce -> + Msg = <<"Query parameter `conflicts` " + "is invalid for reduce views.">>, + throw({query_parse_error, Msg}); + _ -> + Args#view_query_args{conflicts = true} + end; validate_view_query(extra, _Value, Args) -> Args. @@ -438,7 +449,8 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db, UpdateSeq, TotalViewCount, HelperFu } = apply_default_helper_funs(HelperFuns), #view_query_args{ - include_docs = IncludeDocs + include_docs = IncludeDocs, + conflicts = Conflicts } = QueryArgs, fun({{Key, DocId}, Value}, OffsetReds, @@ -456,12 +468,12 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db, UpdateSeq, TotalViewCount, HelperFu {ok, Resp2, RowFunAcc0} = StartRespFun(Req, Etag, TotalViewCount, Offset, RowFunAcc, UpdateSeq), {Go, RowFunAcc2} = SendRowFun(Resp2, Db, {{Key, DocId}, Value}, - IncludeDocs, RowFunAcc0), + IncludeDocs, Conflicts, RowFunAcc0), {Go, {AccLimit - 1, 0, Resp2, RowFunAcc2}}; {AccLimit, _, Resp} when (AccLimit > 0) -> % rendering all other rows {Go, RowFunAcc2} = SendRowFun(Resp, Db, {{Key, DocId}, Value}, - IncludeDocs, RowFunAcc), + IncludeDocs, Conflicts, RowFunAcc), {Go, {AccLimit - 1, 0, Resp, RowFunAcc2}} end end. @@ -537,7 +549,7 @@ apply_default_helper_funs( end, SendRow2 = case SendRow of - undefined -> fun send_json_view_row/5; + undefined -> fun send_json_view_row/6; _ -> SendRow end, @@ -610,8 +622,8 @@ json_view_start_resp(Req, Etag, TotalViewCount, Offset, _Acc, UpdateSeq) -> end, {ok, Resp, BeginBody}. -send_json_view_row(Resp, Db, {{Key, DocId}, Value}, IncludeDocs, RowFront) -> - JsonObj = view_row_obj(Db, {{Key, DocId}, Value}, IncludeDocs), +send_json_view_row(Resp, Db, Kv, IncludeDocs, Conflicts, RowFront) -> + JsonObj = view_row_obj(Db, Kv, IncludeDocs, Conflicts), send_chunk(Resp, RowFront ++ ?JSON_ENCODE(JsonObj)), {ok, ",\r\n"}. @@ -639,10 +651,10 @@ view_etag(_Db, #group{sig=Sig}, #view{update_seq=UpdateSeq, purge_seq=PurgeSeq}, couch_httpd:make_etag({Sig, UpdateSeq, PurgeSeq, Extra}). % the view row has an error -view_row_obj(_Db, {{Key, error}, Value}, _IncludeDocs) -> +view_row_obj(_Db, {{Key, error}, Value}, _IncludeDocs, _Conflicts) -> {[{key, Key}, {error, Value}]}; % include docs in the view output -view_row_obj(Db, {{Key, DocId}, {Props}}, true) -> +view_row_obj(Db, {{Key, DocId}, {Props}}, true, Conflicts) -> Rev = case couch_util:get_value(<<"_rev">>, Props) of undefined -> nil; @@ -650,27 +662,29 @@ view_row_obj(Db, {{Key, DocId}, {Props}}, true) -> couch_doc:parse_rev(Rev0) end, IncludeId = couch_util:get_value(<<"_id">>, Props, DocId), - view_row_with_doc(Db, {{Key, DocId}, {Props}}, {IncludeId, Rev}); -view_row_obj(Db, {{Key, DocId}, Value}, true) -> - view_row_with_doc(Db, {{Key, DocId}, Value}, {DocId, nil}); + view_row_with_doc(Db, {{Key, DocId}, {Props}}, {IncludeId, Rev}, Conflicts); +view_row_obj(Db, {{Key, DocId}, Value}, true, Conflicts) -> + view_row_with_doc(Db, {{Key, DocId}, Value}, {DocId, nil}, Conflicts); % the normal case for rendering a view row -view_row_obj(_Db, {{Key, DocId}, Value}, _IncludeDocs) -> +view_row_obj(_Db, {{Key, DocId}, Value}, _IncludeDocs, _Conflicts) -> {[{id, DocId}, {key, Key}, {value, Value}]}. -view_row_with_doc(Db, {{Key, DocId}, Value}, IdRev) -> - {[{id, DocId}, {key, Key}, {value, Value}] ++ doc_member(Db, IdRev)}. +view_row_with_doc(Db, {{Key, DocId}, Value}, IdRev, Conflicts) -> + {[{id, DocId}, {key, Key}, {value, Value}] ++ + doc_member(Db, IdRev, if Conflicts -> [conflicts]; true -> [] end)}. -doc_member(Db, #doc_info{id = Id, revs = [#rev_info{rev = Rev} | _]} = Info) -> +doc_member(Db, #doc_info{id = Id, revs = [#rev_info{rev = Rev} | _]} = Info, + Options) -> ?LOG_DEBUG("Include Doc: ~p ~p", [Id, Rev]), - case couch_db:open_doc(Db, Info, [deleted]) of + case couch_db:open_doc(Db, Info, [deleted | Options]) of {ok, Doc} -> [{doc, couch_doc:to_json_obj(Doc, [])}]; _ -> [{doc, null}] end; -doc_member(Db, {DocId, Rev}) -> +doc_member(Db, {DocId, Rev}, Options) -> ?LOG_DEBUG("Include Doc: ~p ~p", [DocId, Rev]), - case (catch couch_httpd_db:couch_doc_open(Db, DocId, Rev, [])) of + case (catch couch_httpd_db:couch_doc_open(Db, DocId, Rev, Options)) of #doc{} = Doc -> JsonDoc = couch_doc:to_json_obj(Doc, []), [{doc, JsonDoc}]; |