You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2014/02/15 10:49:56 UTC
[50/59] [abbrv] couchdb commit: updated refs/heads/1994-merge-rcouch
to 6e59a78
remove couch_mrview
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/75150a18
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/75150a18
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/75150a18
Branch: refs/heads/1994-merge-rcouch
Commit: 75150a181219f06e0aebee6538fb655cf6ca2a40
Parents: 4c39d7a
Author: Benoit Chesneau <bc...@gmail.com>
Authored: Thu Feb 13 16:39:28 2014 +0100
Committer: Benoit Chesneau <bc...@gmail.com>
Committed: Thu Feb 13 16:39:28 2014 +0100
----------------------------------------------------------------------
apps/couch_mrview/include/couch_mrview.hrl | 86 --
apps/couch_mrview/src/couch_mrview.app.src | 28 -
apps/couch_mrview/src/couch_mrview.erl | 509 ----------
apps/couch_mrview/src/couch_mrview_changes.erl | 195 ----
apps/couch_mrview/src/couch_mrview_cleanup.erl | 45 -
.../couch_mrview/src/couch_mrview_compactor.erl | 248 -----
apps/couch_mrview/src/couch_mrview_http.erl | 429 ---------
apps/couch_mrview/src/couch_mrview_index.erl | 180 ----
apps/couch_mrview/src/couch_mrview_show.erl | 368 -------
.../couch_mrview/src/couch_mrview_test_util.erl | 111 ---
apps/couch_mrview/src/couch_mrview_updater.erl | 424 ---------
apps/couch_mrview/src/couch_mrview_util.erl | 950 -------------------
apps/couch_mrview/test/01-load.t | 35 -
apps/couch_mrview/test/02-map-views.t | 131 ---
apps/couch_mrview/test/03-red-views.t | 81 --
apps/couch_mrview/test/04-index-info.t | 62 --
apps/couch_mrview/test/05-collation.t | 166 ----
apps/couch_mrview/test/06-all-docs.t | 129 ---
apps/couch_mrview/test/07-compact-swap.t | 62 --
apps/couch_mrview/test/08-changes_since.t | 157 ---
apps/couch_mrview/test/09-index-events.t | 74 --
apps/couch_mrview/test/10-index-changes.t | 217 -----
22 files changed, 4687 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/include/couch_mrview.hrl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/include/couch_mrview.hrl b/apps/couch_mrview/include/couch_mrview.hrl
deleted file mode 100644
index 8c51932..0000000
--- a/apps/couch_mrview/include/couch_mrview.hrl
+++ /dev/null
@@ -1,86 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--record(mrst, {
- sig=nil,
- fd=nil,
- refc,
- db_name,
- idx_name,
- language,
- design_opts=[],
- seq_indexed=false,
- lib,
- views,
- id_btree=nil,
- log_btree=nil,
- update_seq=0,
- purge_seq=0,
- first_build,
- partial_resp_pid,
- doc_acc,
- doc_queue,
- write_queue,
- qserver=nil
-}).
-
-
--record(mrview, {
- id_num,
- update_seq=0,
- purge_seq=0,
- map_names=[],
- reduce_funs=[],
- def,
- btree=nil,
- seq_btree=nil,
- key_byseq_btree=nil,
- seq_indexed=false,
- options=[]
-}).
-
-
--record(mrheader, {
- seq=0,
- purge_seq=0,
- id_btree_state=nil,
- log_btree_state=nil,
- view_states=nil
-}).
-
-
--record(mrargs, {
- view_type,
- reduce,
-
- preflight_fun,
-
- start_key,
- start_key_docid,
- end_key,
- end_key_docid,
- keys,
-
- direction = fwd,
- limit = 16#10000000,
- skip = 0,
- group_level = 0,
- stale = false,
- inclusive_end = true,
- include_docs = false,
- doc_options = [],
- update_seq=false,
- conflicts,
- callback,
- list,
- extra = []
-}).
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview.app.src
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview.app.src b/apps/couch_mrview/src/couch_mrview.app.src
deleted file mode 100644
index cce2c97..0000000
--- a/apps/couch_mrview/src/couch_mrview.app.src
+++ /dev/null
@@ -1,28 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-{application, couch_mrview, [
- {description, "CouchDB Map/Reduce Views"},
- {vsn, "1.3.0"},
- {modules, [
- couch_mrview,
- couch_mrview_compactor,
- couch_mrview_http,
- couch_mrview_index,
- couch_mrview_show,
- couch_mrview_test_util,
- couch_mrview_updater,
- couch_mrview_util
- ]},
- {registered, []},
- {applications, [kernel, stdlib, couch_index]}
-]}.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview.erl b/apps/couch_mrview/src/couch_mrview.erl
deleted file mode 100644
index a578b85..0000000
--- a/apps/couch_mrview/src/couch_mrview.erl
+++ /dev/null
@@ -1,509 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview).
-
--export([query_all_docs/2, query_all_docs/4]).
--export([query_view/3, query_view/4, query_view/6]).
--export([view_changes_since/6, view_changes_since/7]).
--export([count_view_changes_since/4, count_view_changes_since/5]).
--export([get_info/2]).
--export([get_view_info/3]).
--export([refresh/2]).
--export([compact/2, compact/3, cancel_compaction/2]).
--export([cleanup/1]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
--record(mracc, {
- db,
- meta_sent=false,
- total_rows,
- offset,
- limit,
- skip,
- group_level,
- doc_info,
- callback,
- user_acc,
- last_go=ok,
- reduce_fun,
- update_seq,
- args
-}).
-
-
-query_all_docs(Db, Args) ->
- query_all_docs(Db, Args, fun default_cb/2, []).
-
-
-query_all_docs(Db, Args, Callback, Acc) when is_list(Args) ->
- query_all_docs(Db, to_mrargs(Args), Callback, Acc);
-query_all_docs(Db, Args0, Callback, Acc) ->
- Sig = couch_util:with_db(Db, fun(WDb) ->
- {ok, Info} = couch_db:get_db_info(WDb),
- couch_index_util:hexsig(couch_util:md5(term_to_binary(Info)))
- end),
- Args1 = Args0#mrargs{view_type=map},
- Args2 = couch_mrview_util:validate_args(Args1),
- {ok, Acc1} = case Args2#mrargs.preflight_fun of
- PFFun when is_function(PFFun, 2) -> PFFun(Sig, Acc);
- _ -> {ok, Acc}
- end,
- all_docs_fold(Db, Args2, Callback, Acc1).
-
-
-query_view(Db, DDoc, VName) ->
- query_view(Db, DDoc, VName, #mrargs{}).
-
-
-query_view(Db, DDoc, VName, Args) when is_list(Args) ->
- query_view(Db, DDoc, VName, to_mrargs(Args), fun default_cb/2, []);
-query_view(Db, DDoc, VName, Args) ->
- query_view(Db, DDoc, VName, Args, fun default_cb/2, []).
-
-
-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, VInfo, Sig, Args} = couch_mrview_util:get_view(Db, DDoc, VName, Args0),
- {ok, Acc1} = case Args#mrargs.preflight_fun of
- PFFun when is_function(PFFun, 2) -> PFFun(Sig, Acc0);
- _ -> {ok, Acc0}
- end,
- query_view(Db, VInfo, Args, Callback, Acc1).
-
-
-query_view(Db, {Type, View}, Args, Callback, Acc) ->
- case Type of
- map -> map_fold(Db, View, Args, Callback, Acc);
- red -> red_fold(Db, View, Args, Callback, Acc)
- end.
-
-view_changes_since(Db, DDoc, VName, StartSeq, Fun, Acc) ->
- view_changes_since(Db, DDoc, VName, StartSeq, Fun, [], Acc).
-
-view_changes_since(Db, DDoc, VName, StartSeq, Fun, Options, Acc) ->
- Args0 = make_view_changes_args(Options),
- {ok, {_, View}, _, Args} = couch_mrview_util:get_view(Db, DDoc, VName,
- Args0),
- case View#mrview.seq_indexed of
- true ->
- OptList = make_view_changes_opts(StartSeq, Options, Args),
- Btree = case is_key_byseq(Options) of
- true -> View#mrview.key_byseq_btree;
- _ -> View#mrview.seq_btree
- end,
- AccOut = lists:foldl(fun(Opts, Acc0) ->
- {ok, _R, A} = couch_mrview_util:fold_changes(
- Btree, Fun, Acc0, Opts),
- A
- end, Acc, OptList),
- {ok, AccOut};
- _ ->
- {error, seqs_not_indexed}
- end.
-
-
-count_view_changes_since(Db, DDoc, VName, SinceSeq) ->
- count_view_changes_since(Db, DDoc, VName, SinceSeq, []).
-
-count_view_changes_since(Db, DDoc, VName, SinceSeq, Options) ->
- Args0 = make_view_changes_args(Options),
- {ok, {_, View}, _, Args} = couch_mrview_util:get_view(Db, DDoc, VName,
- Args0),
- case View#mrview.seq_indexed of
- true ->
- OptList = make_view_changes_opts(SinceSeq, Options, Args),
- Btree = case is_key_byseq(Options) of
- true -> View#mrview.key_byseq_btree;
- _ -> View#mrview.seq_btree
- end,
- lists:foldl(fun(Opts, Acc0) ->
- {ok, N} = couch_btree:fold_reduce(
- Btree, fun(_SeqStart, PartialReds, 0) ->
- {ok, couch_btree:final_reduce(
- Btree, PartialReds)}
- end,
- 0, Opts),
- Acc0 + N
- end, 0, OptList);
- _ ->
- {error, seqs_not_indexed}
- end.
-
-
-get_info(Db, DDoc) ->
- {ok, Pid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- couch_index:get_info(Pid).
-
-
-%% get informations on a view
-get_view_info(Db, DDoc, VName) ->
- {ok, {_, View}, _, _Args} = couch_mrview_util:get_view(Db, DDoc, VName,
- #mrargs{}),
-
- %% get the total number of rows
- {ok, TotalRows} = couch_mrview_util:get_row_count(View),
-
- %% get the total number of sequence logged in this view
- SeqBtree = View#mrview.seq_btree,
- {ok, TotalSeqs} = case SeqBtree of
- nil -> {ok, 0};
- _ ->
- couch_btree:full_reduce(SeqBtree)
- end,
-
- {ok, [{seq_indexed, View#mrview.seq_indexed},
- {update_seq, View#mrview.update_seq},
- {purge_seq, View#mrview.purge_seq},
- {total_rows, TotalRows},
- {total_seqs, TotalSeqs}]}.
-
-
-
-%% @doc refresh a view index
-refresh(#db{name=DbName}, DDoc) ->
- refresh(DbName, DDoc);
-
-refresh(Db, DDoc) ->
- UpdateSeq = couch_util:with_db(Db, fun(WDb) ->
- couch_db:get_update_seq(WDb)
- end),
-
- case couch_index_server:get_index(couch_mrview_index, Db, DDoc) of
- {ok, Pid} ->
- case catch couch_index:get_state(Pid, UpdateSeq) of
- {ok, _} -> ok;
- Error -> {error, Error}
- end;
- Error ->
- {error, Error}
- end.
-
-compact(Db, DDoc) ->
- compact(Db, DDoc, []).
-
-
-compact(Db, DDoc, Opts) ->
- {ok, Pid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- couch_index:compact(Pid, Opts).
-
-
-cancel_compaction(Db, DDoc) ->
- {ok, IPid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- {ok, CPid} = couch_index:get_compactor_pid(IPid),
- ok = couch_index_compactor:cancel(CPid),
-
- % Cleanup the compaction file if it exists
- {ok, #mrst{sig=Sig, db_name=DbName}} = couch_index:get_state(IPid, 0),
- couch_mrview_util:delete_compaction_file(DbName, Sig),
- ok.
-
-
-cleanup(Db) ->
- couch_mrview_cleanup:run(Db).
-
-
-all_docs_fold(Db, #mrargs{keys=undefined}=Args, Callback, UAcc) ->
- {ok, Info} = couch_db:get_db_info(Db),
- Total = couch_util:get_value(doc_count, Info),
- UpdateSeq = couch_db:get_update_seq(Db),
- Acc = #mracc{
- db=Db,
- total_rows=Total,
- limit=Args#mrargs.limit,
- skip=Args#mrargs.skip,
- callback=Callback,
- user_acc=UAcc,
- reduce_fun=fun couch_mrview_util:all_docs_reduce_to_count/1,
- update_seq=UpdateSeq,
- args=Args
- },
- [Opts] = couch_mrview_util:all_docs_key_opts(Args),
- {ok, Offset, FinalAcc} = couch_db:enum_docs(Db, fun map_fold/3, Acc, Opts),
- finish_fold(FinalAcc, [{total, Total}, {offset, Offset}]);
-all_docs_fold(Db, #mrargs{direction=Dir, keys=Keys0}=Args, Callback, UAcc) ->
- {ok, Info} = couch_db:get_db_info(Db),
- Total = couch_util:get_value(doc_count, Info),
- UpdateSeq = couch_db:get_update_seq(Db),
- Acc = #mracc{
- db=Db,
- total_rows=Total,
- limit=Args#mrargs.limit,
- skip=Args#mrargs.skip,
- callback=Callback,
- user_acc=UAcc,
- reduce_fun=fun couch_mrview_util:all_docs_reduce_to_count/1,
- update_seq=UpdateSeq,
- args=Args
- },
- % Backwards compatibility hack. The old _all_docs iterates keys
- % in reverse if descending=true was passed. Here we'll just
- % reverse the list instead.
- Keys = if Dir =:= fwd -> Keys0; true -> lists:reverse(Keys0) end,
-
- FoldFun = fun(Key, Acc0) ->
- DocInfo = (catch couch_db:get_doc_info(Db, Key)),
- {Doc, Acc1} = case DocInfo of
- {ok, #doc_info{id=Id, revs=[RevInfo | _RestRevs]}=DI} ->
- Rev = couch_doc:rev_to_str(RevInfo#rev_info.rev),
- Props = [{rev, Rev}] ++ case RevInfo#rev_info.deleted of
- true -> [{deleted, true}];
- false -> []
- end,
- {{{Id, Id}, {Props}}, Acc0#mracc{doc_info=DI}};
- not_found ->
- {{{Key, error}, not_found}, Acc0}
- end,
- {_, Acc2} = map_fold(Doc, {[], [{0, 0, 0}]}, Acc1),
- Acc2
- end,
- FinalAcc = lists:foldl(FoldFun, Acc, Keys),
- finish_fold(FinalAcc, [{total, Total}]).
-
-
-map_fold(Db, View, Args, Callback, UAcc) ->
- {ok, Total} = couch_mrview_util:get_row_count(View),
- Acc = #mracc{
- db=Db,
- total_rows=Total,
- limit=Args#mrargs.limit,
- skip=Args#mrargs.skip,
- callback=Callback,
- user_acc=UAcc,
- reduce_fun=fun couch_mrview_util:reduce_to_count/1,
- update_seq=View#mrview.update_seq,
- args=Args
- },
- OptList = couch_mrview_util:key_opts(Args),
- {Reds, Acc2} = lists:foldl(fun(Opts, {_, Acc0}) ->
- {ok, R, A} = couch_mrview_util:fold(View, fun map_fold/3, Acc0, Opts),
- {R, A}
- end, {nil, Acc}, OptList),
- Offset = couch_mrview_util:reduce_to_count(Reds),
- finish_fold(Acc2, [{total, Total}, {offset, Offset}]).
-
-
-map_fold(#full_doc_info{} = FullDocInfo, OffsetReds, Acc) ->
- % matches for _all_docs and translates #full_doc_info{} -> KV pair
- case couch_doc:to_doc_info(FullDocInfo) of
- #doc_info{id=Id, revs=[#rev_info{deleted=false, rev=Rev}|_]} = DI ->
- Value = {[{rev, couch_doc:rev_to_str(Rev)}]},
- map_fold({{Id, Id}, Value}, OffsetReds, Acc#mracc{doc_info=DI});
- #doc_info{revs=[#rev_info{deleted=true}|_]} ->
- {ok, Acc}
- end;
-map_fold(_KV, _Offset, #mracc{skip=N}=Acc) when N > 0 ->
- {ok, Acc#mracc{skip=N-1, last_go=ok}};
-map_fold(KV, OffsetReds, #mracc{offset=undefined}=Acc) ->
- #mracc{
- total_rows=Total,
- callback=Callback,
- user_acc=UAcc0,
- reduce_fun=Reduce,
- update_seq=UpdateSeq,
- args=Args
- } = Acc,
- Offset = Reduce(OffsetReds),
- Meta = make_meta(Args, UpdateSeq, [{total, Total}, {offset, Offset}]),
- {Go, UAcc1} = Callback(Meta, UAcc0),
- Acc1 = Acc#mracc{meta_sent=true, offset=Offset, user_acc=UAcc1, last_go=Go},
- case Go of
- ok -> map_fold(KV, OffsetReds, Acc1);
- stop -> {stop, Acc1}
- end;
-map_fold(_KV, _Offset, #mracc{limit=0}=Acc) ->
- {stop, Acc};
-map_fold({{Key, Id}, Val}, _Offset, Acc) ->
- #mracc{
- db=Db,
- limit=Limit,
- doc_info=DI,
- callback=Callback,
- user_acc=UAcc0,
- args=Args
- } = Acc,
- Doc = case DI of
- #doc_info{} -> couch_mrview_util:maybe_load_doc(Db, DI, Args);
- _ -> couch_mrview_util:maybe_load_doc(Db, Id, Val, Args)
- end,
- Row = [{id, Id}, {key, Key}, {value, Val}] ++ Doc,
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{
- limit=Limit-1,
- doc_info=undefined,
- user_acc=UAcc1,
- last_go=Go
- }}.
-
-
-red_fold(Db, {_Nth, _Lang, View}=RedView, Args, Callback, UAcc) ->
- Acc = #mracc{
- db=Db,
- total_rows=null,
- limit=Args#mrargs.limit,
- skip=Args#mrargs.skip,
- group_level=Args#mrargs.group_level,
- callback=Callback,
- user_acc=UAcc,
- update_seq=View#mrview.update_seq,
- args=Args
- },
- GroupFun = group_rows_fun(Args#mrargs.group_level),
- OptList = couch_mrview_util:key_opts(Args, [{key_group_fun, GroupFun}]),
- Acc2 = lists:foldl(fun(Opts, Acc0) ->
- {ok, Acc1} =
- couch_mrview_util:fold_reduce(RedView, fun red_fold/3, Acc0, Opts),
- Acc1
- end, Acc, OptList),
- finish_fold(Acc2, []).
-
-red_fold(_Key, _Red, #mracc{skip=N}=Acc) when N > 0 ->
- {ok, Acc#mracc{skip=N-1, last_go=ok}};
-red_fold(Key, Red, #mracc{meta_sent=false}=Acc) ->
- #mracc{
- args=Args,
- callback=Callback,
- user_acc=UAcc0,
- update_seq=UpdateSeq
- } = Acc,
- Meta = make_meta(Args, UpdateSeq, []),
- {Go, UAcc1} = Callback(Meta, UAcc0),
- Acc1 = Acc#mracc{user_acc=UAcc1, meta_sent=true, last_go=Go},
- case Go of
- ok -> red_fold(Key, Red, Acc1);
- _ -> {Go, Acc1}
- end;
-red_fold(_Key, _Red, #mracc{limit=0} = Acc) ->
- {stop, Acc};
-red_fold(_Key, Red, #mracc{group_level=0} = Acc) ->
- #mracc{
- limit=Limit,
- callback=Callback,
- user_acc=UAcc0
- } = Acc,
- Row = [{key, null}, {value, Red}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc=UAcc1, limit=Limit-1, last_go=Go}};
-red_fold(Key, Red, #mracc{group_level=exact} = Acc) ->
- #mracc{
- limit=Limit,
- callback=Callback,
- user_acc=UAcc0
- } = Acc,
- Row = [{key, Key}, {value, Red}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc=UAcc1, limit=Limit-1, last_go=Go}};
-red_fold(K, Red, #mracc{group_level=I} = Acc) when I > 0, is_list(K) ->
- #mracc{
- limit=Limit,
- callback=Callback,
- user_acc=UAcc0
- } = Acc,
- Row = [{key, lists:sublist(K, I)}, {value, Red}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc=UAcc1, limit=Limit-1, last_go=Go}};
-red_fold(K, Red, #mracc{group_level=I} = Acc) when I > 0 ->
- #mracc{
- limit=Limit,
- callback=Callback,
- user_acc=UAcc0
- } = Acc,
- Row = [{key, K}, {value, Red}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc=UAcc1, limit=Limit-1, last_go=Go}}.
-
-
-finish_fold(#mracc{last_go=ok, update_seq=UpdateSeq}=Acc, ExtraMeta) ->
- #mracc{callback=Callback, user_acc=UAcc, args=Args}=Acc,
- % Possible send meta info
- Meta = make_meta(Args, UpdateSeq, ExtraMeta),
- {Go, UAcc1} = case Acc#mracc.meta_sent of
- false -> Callback(Meta, UAcc);
- _ -> {ok, Acc#mracc.user_acc}
- end,
- % Notify callback that the fold is complete.
- {_, UAcc2} = case Go of
- ok -> Callback(complete, UAcc1);
- _ -> {ok, UAcc1}
- end,
- {ok, UAcc2};
-finish_fold(#mracc{user_acc=UAcc}, _ExtraMeta) ->
- {ok, UAcc}.
-
-
-make_meta(Args, UpdateSeq, Base) ->
- case Args#mrargs.update_seq of
- true -> {meta, Base ++ [{update_seq, UpdateSeq}]};
- _ -> {meta, Base}
- end.
-
-
-group_rows_fun(exact) ->
- fun({Key1,_}, {Key2,_}) -> Key1 == Key2 end;
-group_rows_fun(0) ->
- fun(_A, _B) -> true end;
-group_rows_fun(GroupLevel) when is_integer(GroupLevel) ->
- fun({[_|_] = Key1,_}, {[_|_] = Key2,_}) ->
- lists:sublist(Key1, GroupLevel) == lists:sublist(Key2, GroupLevel);
- ({Key1,_}, {Key2,_}) ->
- Key1 == Key2
- end.
-
-
-default_cb(complete, Acc) ->
- {ok, lists:reverse(Acc)};
-default_cb({final, Info}, []) ->
- {ok, [Info]};
-default_cb({final, _}, Acc) ->
- {ok, Acc};
-default_cb(Row, Acc) ->
- {ok, [Row | Acc]}.
-
-
-to_mrargs(KeyList) ->
- lists:foldl(fun({Key, Value}, Acc) ->
- Index = lookup_index(couch_util:to_existing_atom(Key)),
- setelement(Index, Acc, Value)
- end, #mrargs{}, KeyList).
-
-
-lookup_index(Key) ->
- Index = lists:zip(
- record_info(fields, mrargs), lists:seq(2, record_info(size, mrargs))
- ),
- couch_util:get_value(Key, Index).
-
-
-is_key_byseq(Options) ->
- lists:any(fun({K, _}) ->
- lists:member(K, [start_key, end_key, start_key_docid,
- end_key_docid, keys])
- end, Options).
-
-make_view_changes_args(Options) ->
- case is_key_byseq(Options) of
- true ->
- to_mrargs(Options);
- false ->
- #mrargs{}
- end.
-
-make_view_changes_opts(StartSeq, Options, Args) ->
- case is_key_byseq(Options) of
- true ->
- couch_mrview_util:changes_key_opts(StartSeq, Args);
- false ->
- [[{start_key, {StartSeq+1, <<>>}}] ++ Options]
- end.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_changes.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_changes.erl b/apps/couch_mrview/src/couch_mrview_changes.erl
deleted file mode 100644
index c31624e..0000000
--- a/apps/couch_mrview/src/couch_mrview_changes.erl
+++ /dev/null
@@ -1,195 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-%
--module(couch_mrview_changes).
-
--export([handle_changes/6]).
-
--include_lib("couch/include/couch_db.hrl").
-
--record(vst, {dbname,
- ddoc,
- view,
- view_options,
- since,
- callback,
- acc,
- user_timeout,
- timeout,
- heartbeat,
- timeout_acc=0,
- notifier,
- stream,
- refresh}).
-
--type changes_stream() :: true | false | once.
--type changes_options() :: [{stream, changes_stream()} |
- {since, integer()} |
- {view_options, list()} |
- {timeout, integer()} |
- {heartbeat, true | integer()} |
- {refresh, true | false}].
-
--export_type([changes_stream/0]).
--export_type([changes_options/0]).
-
-%% @doc function returning changes in a streaming fashion if needed.
--spec handle_changes(binary(), binary(), binary(), function(), term(),
- changes_options()) -> ok | {error, term()}.
-handle_changes(DbName, DDocId, View, Fun, Acc, Options) ->
- Since = proplists:get_value(since, Options, 0),
- Stream = proplists:get_value(stream, Options, false),
- ViewOptions = proplists:get_value(view_options, Options, []),
- Refresh = proplists:get_value(refresh, Options, false),
-
- State0 = #vst{dbname=DbName,
- ddoc=DDocId,
- view=View,
- view_options=ViewOptions,
- since=Since,
- callback=Fun,
- acc=Acc},
-
- maybe_acquire_indexer(Refresh, DbName, DDocId),
- try
- case view_changes_since(State0) of
- {ok, #vst{since=LastSeq, acc=Acc2}=State} ->
- case Stream of
- true ->
- start_loop(State#vst{stream=true}, Options);
- once when LastSeq =:= Since ->
- start_loop(State#vst{stream=once}, Options);
- _ ->
- Fun(stop, {LastSeq, Acc2})
- end;
- {stop, #vst{since=LastSeq, acc=Acc2}} ->
- Fun(stop, {LastSeq, Acc2});
- Error ->
- Error
- end
- after
- maybe_release_indexer(Refresh, DbName, DDocId)
- end.
-
-start_loop(#vst{dbname=DbName, ddoc=DDocId}=State, Options) ->
- {UserTimeout, Timeout, Heartbeat} = changes_timeout(Options),
- Notifier = index_update_notifier(DbName, DDocId),
- try
- loop(State#vst{notifier=Notifier,
- user_timeout=UserTimeout,
- timeout=Timeout,
- heartbeat=Heartbeat})
- after
- couch_index_event:stop(Notifier)
- end.
-
-loop(#vst{since=Since, callback=Callback, acc=Acc,
- user_timeout=UserTimeout, timeout=Timeout,
- heartbeat=Heartbeat, timeout_acc=TimeoutAcc,
- stream=Stream}=State) ->
- receive
- index_update ->
- case view_changes_since(State) of
- {ok, State2} when Stream =:= true ->
- loop(State2#vst{timeout_acc=0});
- {ok, #vst{since=LastSeq, acc=Acc2}} ->
- Callback(stop, {LastSeq, Acc2});
- {stop, #vst{since=LastSeq, acc=Acc2}} ->
- Callback(stop, {LastSeq, Acc2})
- end;
- index_delete ->
- Callback(stop, {Since, Acc})
- after Timeout ->
- TimeoutAcc2 = TimeoutAcc + Timeout,
- case UserTimeout =< TimeoutAcc2 of
- true ->
- Callback(stop, {Since, Acc});
- false when Heartbeat =:= true ->
- case Callback(heartbeat, Acc) of
- {ok, Acc2} ->
- loop(State#vst{acc=Acc2, timeout_acc=TimeoutAcc2});
- {stop, Acc2} ->
- Callback(stop, {Since, Acc2})
- end;
- _ ->
- Callback(stop, {Since, Acc})
- end
- end.
-
-changes_timeout(Options) ->
- DefaultTimeout = list_to_integer(
- couch_config:get("httpd", "changes_timeout", "60000")
- ),
- UserTimeout = proplists:get_value(timeout, Options, DefaultTimeout),
- {Timeout, Heartbeat} = case proplists:get_value(heartbeat, Options) of
- undefined -> {UserTimeout, false};
- true ->
- T = erlang:min(DefaultTimeout, UserTimeout),
- {T, true};
- H ->
- T = erlang:min(H, UserTimeout),
- {T, true}
- end,
- {UserTimeout, Timeout, Heartbeat}.
-
-view_changes_since(#vst{dbname=DbName, ddoc=DDocId, view=View,
- view_options=Options, since=Since,
- callback=Callback, acc=UserAcc}=State) ->
- Wrapper = fun ({{Seq, _Key, _DocId}, _Val}=KV, {Go, Acc2, OldSeq}) ->
- LastSeq = if OldSeq < Seq -> Seq;
- true -> OldSeq
- end,
-
- {Go, Acc3} = Callback(KV, Acc2),
- {Go, {Go, Acc3, LastSeq}}
- end,
-
- Acc0 = {ok, UserAcc, Since},
- case couch_mrview:view_changes_since(DbName, DDocId, View, Since,
- Wrapper, Options, Acc0) of
- {ok, {Go, UserAcc2, Since2}}->
- {Go, State#vst{since=Since2, acc=UserAcc2}};
- Error ->
- Error
- end.
-
-index_update_notifier(#db{name=DbName}, DDocId) ->
- index_update_notifier(DbName, DDocId);
-index_update_notifier(DbName, DDocId) ->
- Self = self(),
- {ok, NotifierPid} = couch_index_event:start_link(fun
- ({index_update, {Name, Id, couch_mrview_index}})
- when Name =:= DbName, Id =:= DDocId ->
- Self ! index_update;
- ({index_delete, {Name, Id, couch_mrview_index}})
- when Name =:= DbName, Id =:= DDocId ->
- Self ! index_delete;
- (_) ->
- ok
- end),
- NotifierPid.
-
-%% acquire the background indexing task so it can eventually be started
-%% if the process close the background task will be automatically
-%% released.
-maybe_acquire_indexer(false, _, _) ->
- ok;
-maybe_acquire_indexer(true, DbName, DDocId) ->
- couch_index_server:acquire_indexer(couch_mrview_index, DbName,
- DDocId).
-
-%% release the background indexing task so it can eventually be stopped
-maybe_release_indexer(false, _, _) ->
- ok;
-maybe_release_indexer(true, DbName, DDocId) ->
- couch_index_server:release_indexer(couch_mrview_index, DbName,
- DDocId).
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_cleanup.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_cleanup.erl b/apps/couch_mrview/src/couch_mrview_cleanup.erl
deleted file mode 100644
index 0593570..0000000
--- a/apps/couch_mrview/src/couch_mrview_cleanup.erl
+++ /dev/null
@@ -1,45 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%-include_lib("couch/include/couch_db.hrl").
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_cleanup).
-
--export([run/1]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
-run(Db) ->
- RootDir = couch_index_util:root_dir(),
- DbName = couch_db:name(Db),
-
- DesignDocs = couch_db:get_design_docs(Db),
- SigFiles = lists:foldl(fun(DDocInfo, SFAcc) ->
- {ok, DDoc} = couch_db:open_doc_int(Db, DDocInfo, [ejson_body]),
- {ok, InitState} = couch_mrview_util:ddoc_to_mrst(DbName, DDoc),
- Sig = InitState#mrst.sig,
- IFName = couch_mrview_util:index_file(DbName, Sig),
- CFName = couch_mrview_util:compaction_file(DbName, Sig),
- [IFName, CFName | SFAcc]
- end, [], [DD || DD <- DesignDocs, DD#full_doc_info.deleted == false]),
-
- IdxDir = couch_index_util:index_dir(mrview, DbName),
- DiskFiles = filelib:wildcard(filename:join(IdxDir, "*")),
-
- % We need to delete files that have no ddoc.
- ToDelete = DiskFiles -- SigFiles,
-
- lists:foreach(fun(FN) ->
- ?LOG_DEBUG("Deleting stale view file: ~s", [FN]),
- couch_file:delete(RootDir, FN, false)
- end, ToDelete),
-
- ok.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_compactor.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_compactor.erl b/apps/couch_mrview/src/couch_mrview_compactor.erl
deleted file mode 100644
index a7e594f..0000000
--- a/apps/couch_mrview/src/couch_mrview_compactor.erl
+++ /dev/null
@@ -1,248 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_compactor).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
--export([compact/3, swap_compacted/2]).
-
--record(acc, {
- btree = nil,
- last_id = nil,
- kvs = [],
- kvs_size = 0,
- changes = 0,
- total_changes
-}).
-
-
-compact(_Db, State, Opts) ->
- case lists:member(recompact, Opts) of
- false -> compact(State);
- true -> recompact(State)
- end.
-
-compact(State) ->
- #mrst{
- db_name=DbName,
- idx_name=IdxName,
- sig=Sig,
- update_seq=Seq,
- id_btree=IdBtree,
- log_btree=LogBtree,
- seq_indexed=SeqIndexed,
- views=Views
- } = State,
-
- {EmptyState, NumDocIds} = couch_util:with_db(DbName, fun(Db) ->
- CompactFName = couch_mrview_util:compaction_file(DbName, Sig),
- {ok, Fd} = couch_mrview_util:open_file(CompactFName),
- ESt = couch_mrview_util:reset_index(Db, Fd, State),
-
- {ok, DbReduce} = couch_btree:full_reduce(Db#db.fulldocinfo_by_id_btree),
- Count = element(1, DbReduce),
-
- {ESt, Count}
- end),
-
- #mrst{
- id_btree = EmptyIdBtree,
- log_btree = EmptyLogBtree,
- views = EmptyViews
- } = EmptyState,
-
- TotalChanges0 = case SeqIndexed of
- true -> NumDocIds * 2;
- _ -> NumDocIds
- end,
-
- TotalChanges = lists:foldl(
- fun(View, Acc) ->
- {ok, Kvs} = couch_mrview_util:get_row_count(View),
- case SeqIndexed of
- true ->
- {ok, SKvs} = couch_mrview_util:get_view_changes_count(View),
- Acc + Kvs + SKvs * 2;
- false ->
- Acc + Kvs
- end
- end,
- TotalChanges0, Views),
-
- couch_task_status:add_task([
- {type, view_compaction},
- {database, DbName},
- {design_document, IdxName},
- {progress, 0}
- ]),
-
- BufferSize0 = couch_config:get(
- "view_compaction", "keyvalue_buffer_size", "2097152"
- ),
- BufferSize = list_to_integer(BufferSize0),
-
- FoldFun = fun({DocId, _} = KV, Acc) ->
- #acc{btree = Bt, kvs = Kvs, kvs_size = KvsSize, last_id = LastId} = Acc,
- if DocId =:= LastId ->
- % COUCHDB-999 regression test
- ?LOG_ERROR("Duplicate docid `~s` detected in view group `~s`"
- ++ ", database `~s` - This view needs to be rebuilt.",
- [DocId, IdxName, DbName]
- ),
- exit({view_duplicate_id, DocId});
- true -> ok end,
- KvsSize2 = KvsSize + ?term_size(KV),
- case KvsSize2 >= BufferSize of
- true ->
- {ok, Bt2} = couch_btree:add(Bt, lists:reverse([KV | Kvs])),
- Acc2 = update_task(Acc, 1 + length(Kvs)),
- {ok, Acc2#acc{
- btree = Bt2, kvs = [], kvs_size = 0, last_id = DocId}};
- _ ->
- {ok, Acc#acc{
- kvs = [KV | Kvs], kvs_size = KvsSize2, last_id = DocId}}
- end
- end,
-
- InitAcc = #acc{total_changes = TotalChanges, btree = EmptyIdBtree},
- {ok, _, FinalAcc} = couch_btree:foldl(IdBtree, FoldFun, InitAcc),
- #acc{btree = Bt3, kvs = Uncopied} = FinalAcc,
- {ok, NewIdBtree} = couch_btree:add(Bt3, lists:reverse(Uncopied)),
- FinalAcc2 = update_task(FinalAcc, length(Uncopied)),
-
-
- {NewLogBtree, FinalAcc3} = case SeqIndexed of
- true ->
- compact_log(LogBtree, BufferSize,
- FinalAcc2#acc{kvs=[],
- kvs_size=0,
- btree=EmptyLogBtree});
- _ ->
- {nil, FinalAcc2}
- end,
-
- {NewViews, _} = lists:mapfoldl(fun({View, EmptyView}, Acc) ->
- compact_view(View, EmptyView, BufferSize, Acc)
- end, FinalAcc3, lists:zip(Views, EmptyViews)),
-
- unlink(EmptyState#mrst.fd),
- {ok, EmptyState#mrst{
- id_btree=NewIdBtree,
- log_btree=NewLogBtree,
- views=NewViews,
- update_seq=Seq
- }}.
-
-
-recompact(State) ->
- link(State#mrst.fd),
- {Pid, Ref} = erlang:spawn_monitor(fun() ->
- couch_index_updater:update(couch_mrview_index, State)
- end),
- receive
- {'DOWN', Ref, _, _, {updated, Pid, State2}} ->
- unlink(State#mrst.fd),
- {ok, State2}
- end.
-
-compact_log(LogBtree, BufferSize, Acc0) ->
- FoldFun = fun({DocId, _} = KV, Acc) ->
- #acc{btree = Bt, kvs = Kvs, kvs_size = KvsSize} = Acc,
- KvsSize2 = KvsSize + ?term_size(KV),
- case KvsSize2 >= BufferSize of
- true ->
- {ok, Bt2} = couch_btree:add(Bt, lists:reverse([KV | Kvs])),
- Acc2 = update_task(Acc, 1 + length(Kvs)),
- {ok, Acc2#acc{
- btree = Bt2, kvs = [], kvs_size = 0}};
- _ ->
- {ok, Acc#acc{
- kvs = [KV | Kvs], kvs_size = KvsSize2}}
- end
- end,
-
- {ok, _, FinalAcc} = couch_btree:foldl(LogBtree, FoldFun, Acc0),
- #acc{btree = Bt3, kvs = Uncopied} = FinalAcc,
- {ok, NewLogBtree} = couch_btree:add(Bt3, lists:reverse(Uncopied)),
- FinalAcc2 = update_task(FinalAcc, length(Uncopied)),
- {NewLogBtree, FinalAcc2}.
-
-%% @spec compact_view(View, EmptyView, Retry, Acc) -> {CompactView, NewAcc}
-compact_view(View, EmptyView, BufferSize, Acc0) ->
-
- {NewBt, Acc1} = compact_view_btree(View#mrview.btree,
- EmptyView#mrview.btree,
- BufferSize, Acc0),
-
- %% are we indexing changes by sequences?
- {NewSeqBt, NewKeyBySeqBt, FinalAcc} = case View#mrview.seq_indexed of
- true ->
- {SBt, Acc2} = compact_view_btree(View#mrview.seq_btree,
- EmptyView#mrview.seq_btree,
- BufferSize, Acc1),
- {KSBt, Acc3} = compact_view_btree(View#mrview.key_byseq_btree,
- EmptyView#mrview.key_byseq_btree,
- BufferSize, Acc2),
- {SBt, KSBt, Acc3};
- _ ->
- {nil, nil, Acc1}
- end,
- {EmptyView#mrview{btree=NewBt,
- seq_btree=NewSeqBt,
- key_byseq_btree=NewKeyBySeqBt}, FinalAcc}.
-
-compact_view_btree(Btree, EmptyBtree, BufferSize, Acc0) ->
- Fun = fun(KV, #acc{btree = Bt, kvs = Kvs, kvs_size = KvsSize} = Acc) ->
- KvsSize2 = KvsSize + ?term_size(KV),
- if KvsSize2 >= BufferSize ->
- {ok, Bt2} = couch_btree:add(Bt, lists:reverse([KV | Kvs])),
- Acc2 = update_task(Acc, 1 + length(Kvs)),
- {ok, Acc2#acc{btree = Bt2, kvs = [], kvs_size = 0}};
- true ->
- {ok, Acc#acc{kvs = [KV | Kvs], kvs_size = KvsSize2}}
- end
- end,
-
- InitAcc = Acc0#acc{kvs = [], kvs_size = 0, btree = EmptyBtree},
- {ok, _, FinalAcc} = couch_btree:foldl(Btree, Fun, InitAcc),
- #acc{btree = Bt3, kvs = Uncopied} = FinalAcc,
- {ok, NewBt} = couch_btree:add(Bt3, lists:reverse(Uncopied)),
- FinalAcc2 = update_task(FinalAcc, length(Uncopied)),
- {NewBt, FinalAcc2}.
-
-update_task(#acc{changes = Changes, total_changes = Total} = Acc, ChangesInc) ->
- Changes2 = Changes + ChangesInc,
- couch_task_status:update([{progress, (Changes2 * 100) div Total}]),
- Acc#acc{changes = Changes2}.
-
-
-swap_compacted(OldState, NewState) ->
- #mrst{
- sig=Sig,
- db_name=DbName
- } = NewState,
-
- link(NewState#mrst.fd),
-
- RootDir = couch_index_util:root_dir(),
- IndexFName = couch_mrview_util:index_file(DbName, Sig),
- CompactFName = couch_mrview_util:compaction_file(DbName, Sig),
- ok = couch_file:delete(RootDir, IndexFName),
- ok = file:rename(CompactFName, IndexFName),
-
- unlink(OldState#mrst.fd),
- couch_ref_counter:drop(OldState#mrst.refc),
- {ok, NewRefCounter} = couch_ref_counter:start([NewState#mrst.fd]),
-
- {ok, NewState#mrst{refc=NewRefCounter}}.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_http.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_http.erl b/apps/couch_mrview/src/couch_mrview_http.erl
deleted file mode 100644
index 64c9fb1..0000000
--- a/apps/couch_mrview/src/couch_mrview_http.erl
+++ /dev/null
@@ -1,429 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_http).
-
--export([
- handle_all_docs_req/2,
- handle_view_req/3,
- handle_temp_view_req/2,
- handle_info_req/3,
- handle_compact_req/3,
- handle_cleanup_req/2,
- parse_qs/2
-]).
-
--export([parse_boolean/1,
- parse_int/1,
- parse_pos_int/1]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
-
--record(vacc, {
- db,
- req,
- resp,
- prepend,
- etag
-}).
-
-
-handle_all_docs_req(#httpd{method='GET'}=Req, Db) ->
- all_docs_req(Req, Db, undefined);
-handle_all_docs_req(#httpd{method='POST'}=Req, Db) ->
- Keys = get_view_keys(couch_httpd:json_body_obj(Req)),
- all_docs_req(Req, Db, Keys);
-handle_all_docs_req(Req, _Db) ->
- couch_httpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-
-handle_view_req(#httpd{method='GET',
- path_parts=[_, _, DDocName, _, VName, <<"_info">>]}=Req,
- Db, _DDoc) ->
-
- DDocId = <<"_design/", DDocName/binary >>,
- {ok, Info} = couch_mrview:get_view_info(Db#db.name, DDocId, VName),
-
- FinalInfo = [{db_name, Db#db.name},
- {ddoc, DDocId},
- {view, VName}] ++ Info,
- couch_httpd:send_json(Req, 200, {FinalInfo});
-
-handle_view_req(#httpd{method='GET'}=Req, Db, DDoc) ->
- [_, _, _, _, ViewName] = Req#httpd.path_parts,
- couch_stats_collector:increment({httpd, view_reads}),
- design_doc_view(Req, Db, DDoc, ViewName, undefined);
-handle_view_req(#httpd{method='POST'}=Req, Db, DDoc) ->
- [_, _, _, _, ViewName] = Req#httpd.path_parts,
- Keys = get_view_keys(couch_httpd:json_body_obj(Req)),
- couch_stats_collector:increment({httpd, view_reads}),
- design_doc_view(Req, Db, DDoc, ViewName, Keys);
-handle_view_req(Req, _Db, _DDoc) ->
- couch_httpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-
-handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
- couch_httpd:validate_ctype(Req, "application/json"),
- ok = couch_db:check_is_admin(Db),
- {Body} = couch_httpd:json_body_obj(Req),
- DDoc = couch_mrview_util:temp_view_to_ddoc({Body}),
- Keys = get_view_keys({Body}),
- couch_stats_collector:increment({httpd, temporary_view_reads}),
- design_doc_view(Req, Db, DDoc, <<"temp">>, Keys);
-handle_temp_view_req(Req, _Db) ->
- couch_httpd:send_method_not_allowed(Req, "POST").
-
-
-handle_info_req(#httpd{method='GET'}=Req, Db, DDoc) ->
- [_, _, Name, _] = Req#httpd.path_parts,
- {ok, Info} = couch_mrview:get_info(Db, DDoc),
- couch_httpd:send_json(Req, 200, {[
- {name, Name},
- {view_index, {Info}}
- ]});
-handle_info_req(Req, _Db, _DDoc) ->
- couch_httpd:send_method_not_allowed(Req, "GET").
-
-
-handle_compact_req(#httpd{method='POST'}=Req, Db, DDoc) ->
- ok = couch_db:check_is_admin(Db),
- couch_httpd:validate_ctype(Req, "application/json"),
- ok = couch_mrview:compact(Db, DDoc),
- couch_httpd:send_json(Req, 202, {[{ok, true}]});
-handle_compact_req(Req, _Db, _DDoc) ->
- couch_httpd:send_method_not_allowed(Req, "POST").
-
-
-handle_cleanup_req(#httpd{method='POST'}=Req, Db) ->
- ok = couch_db:check_is_admin(Db),
- couch_httpd:validate_ctype(Req, "application/json"),
- ok = couch_mrview:cleanup(Db),
- couch_httpd:send_json(Req, 202, {[{ok, true}]});
-handle_cleanup_req(Req, _Db) ->
- couch_httpd:send_method_not_allowed(Req, "POST").
-
-
-all_docs_req(Req, Db, Keys) ->
- case couch_db:is_system_db(Db) of
- true ->
- case (catch couch_db:check_is_admin(Db)) of
- ok ->
- do_all_docs_req(Req, Db, Keys);
- _ ->
- DbName = ?b2l(Db#db.name),
- case couch_config:get("couch_httpd_auth",
- "authentication_db",
- "_users") of
- DbName ->
- UsersDbPublic = couch_config:get("couch_httpd_auth", "users_db_public", "false"),
- PublicFields = couch_config:get("couch_httpd_auth", "public_fields"),
- case {UsersDbPublic, PublicFields} of
- {"true", PublicFields} when PublicFields =/= undefined ->
- do_all_docs_req(Req, Db, Keys);
- {_, _} ->
- throw({forbidden, <<"Only admins can access _all_docs",
- " of system databases.">>})
- end;
- _ ->
- throw({forbidden, <<"Only admins can access _all_docs",
- " of system databases.">>})
- end
- end;
- false ->
- do_all_docs_req(Req, Db, Keys)
- end.
-
-do_all_docs_req(Req, Db, Keys) ->
- Args0 = parse_qs(Req, Keys),
- ETagFun = fun(Sig, Acc0) ->
- ETag = couch_httpd:make_etag(Sig),
- case couch_httpd:etag_match(Req, ETag) of
- true -> throw({etag_match, ETag});
- false -> {ok, Acc0#vacc{etag=ETag}}
- end
- end,
- Args = Args0#mrargs{preflight_fun=ETagFun},
- {ok, Resp} = couch_httpd:etag_maybe(Req, fun() ->
- VAcc0 = #vacc{db=Db, req=Req},
- DbName = ?b2l(Db#db.name),
- UsersDbName = couch_config:get("couch_httpd_auth",
- "authentication_db",
- "_users"),
- IsAdmin = is_admin(Db),
- Callback = get_view_callback(DbName, UsersDbName, IsAdmin),
- couch_mrview:query_all_docs(Db, Args, Callback, VAcc0)
- end),
- case is_record(Resp, vacc) of
- true -> {ok, Resp#vacc.resp};
- _ -> {ok, Resp}
- end.
-
-is_admin(Db) ->
- case catch couch_db:check_is_admin(Db) of
- {unauthorized, _} ->
- false;
- ok ->
- true
- end.
-
-
-% admin users always get all fields
-get_view_callback(_, _, true) ->
- fun view_cb/2;
-% if we are operating on the users db and we aren't
-% admin, filter the view
-get_view_callback(_DbName, _DbName, false) ->
- fun filtered_view_cb/2;
-% non _users databases get all fields
-get_view_callback(_, _, _) ->
- fun view_cb/2.
-
-
-design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
- Args0 = parse_qs(Req, Keys),
- ETagFun = fun(Sig, Acc0) ->
- ETag = couch_httpd:make_etag(Sig),
- case couch_httpd:etag_match(Req, ETag) of
- true -> throw({etag_match, ETag});
- false -> {ok, Acc0#vacc{etag=ETag}}
- end
- end,
- Args = Args0#mrargs{preflight_fun=ETagFun},
- {ok, Resp} = couch_httpd:etag_maybe(Req, fun() ->
- VAcc0 = #vacc{db=Db, req=Req},
- couch_mrview:query_view(Db, DDoc, ViewName, Args, fun view_cb/2, VAcc0)
- end),
- case is_record(Resp, vacc) of
- true -> {ok, Resp#vacc.resp};
- _ -> {ok, Resp}
- end.
-
-
-filtered_view_cb({row, Row0}, Acc) ->
- Row1 = lists:map(fun({doc, null}) ->
- {doc, null};
- ({doc, Body}) ->
- Doc = couch_users_db:strip_non_public_fields(#doc{body=Body}),
- {doc, Doc#doc.body};
- (KV) ->
- KV
- end, Row0),
- view_cb({row, Row1}, Acc);
-filtered_view_cb(Obj, Acc) ->
- view_cb(Obj, Acc).
-
-
-view_cb({meta, Meta}, #vacc{resp=undefined}=Acc) ->
- Headers = [{"ETag", Acc#vacc.etag}],
- {ok, Resp} = couch_httpd:start_json_response(Acc#vacc.req, 200, Headers),
- % Map function starting
- Parts = case couch_util:get_value(total, Meta) of
- undefined -> [];
- Total -> [io_lib:format("\"total_rows\":~p", [Total])]
- end ++ case couch_util:get_value(offset, Meta) of
- undefined -> [];
- Offset -> [io_lib:format("\"offset\":~p", [Offset])]
- end ++ case couch_util:get_value(update_seq, Meta) of
- undefined -> [];
- UpdateSeq -> [io_lib:format("\"update_seq\":~p", [UpdateSeq])]
- end ++ ["\"rows\":["],
- Chunk = lists:flatten("{" ++ string:join(Parts, ",") ++ "\r\n"),
- couch_httpd:send_chunk(Resp, Chunk),
- {ok, Acc#vacc{resp=Resp, prepend=""}};
-view_cb({row, Row}, #vacc{resp=undefined}=Acc) ->
- % Reduce function starting
- Headers = [{"ETag", Acc#vacc.etag}],
- {ok, Resp} = couch_httpd:start_json_response(Acc#vacc.req, 200, Headers),
- couch_httpd:send_chunk(Resp, ["{\"rows\":[\r\n", row_to_json(Row)]),
- {ok, #vacc{resp=Resp, prepend=",\r\n"}};
-view_cb({row, Row}, Acc) ->
- % Adding another row
- couch_httpd:send_chunk(Acc#vacc.resp, [Acc#vacc.prepend, row_to_json(Row)]),
- {ok, Acc#vacc{prepend=",\r\n"}};
-view_cb(complete, #vacc{resp=undefined}=Acc) ->
- % Nothing in view
- {ok, Resp} = couch_httpd:send_json(Acc#vacc.req, 200, {[{rows, []}]}),
- {ok, Acc#vacc{resp=Resp}};
-view_cb(complete, Acc) ->
- % Finish view output
- couch_httpd:send_chunk(Acc#vacc.resp, "\r\n]}"),
- couch_httpd:end_json_response(Acc#vacc.resp),
- {ok, Acc}.
-
-
-row_to_json(Row) ->
- Id = couch_util:get_value(id, Row),
- row_to_json(Id, Row).
-
-
-row_to_json(error, Row) ->
- % Special case for _all_docs request with KEYS to
- % match prior behavior.
- Key = couch_util:get_value(key, Row),
- Val = couch_util:get_value(value, Row),
- Obj = {[{key, Key}, {error, Val}]},
- ?JSON_ENCODE(Obj);
-row_to_json(Id0, Row) ->
- Id = case Id0 of
- undefined -> [];
- Id0 -> [{id, Id0}]
- end,
- Key = couch_util:get_value(key, Row, null),
- Val = couch_util:get_value(value, Row),
- Doc = case couch_util:get_value(doc, Row) of
- undefined -> [];
- Doc0 -> [{doc, Doc0}]
- end,
- Obj = {Id ++ [{key, Key}, {value, Val}] ++ Doc},
- ?JSON_ENCODE(Obj).
-
-
-get_view_keys({Props}) ->
- case couch_util:get_value(<<"keys">>, Props) of
- undefined ->
- ?LOG_DEBUG("POST with no keys member.", []),
- undefined;
- Keys when is_list(Keys) ->
- Keys;
- _ ->
- throw({bad_request, "`keys` member must be a array."})
- end.
-
-
-parse_qs(Req, Keys) ->
- Args = #mrargs{keys=Keys},
- lists:foldl(fun({K, V}, Acc) ->
- parse_qs(K, V, Acc)
- end, Args, couch_httpd:qs(Req)).
-
-
-parse_qs(Key, Val, Args) ->
- case Key of
- "" ->
- Args;
- "reduce" ->
- Args#mrargs{reduce=parse_boolean(Val)};
- "key" ->
- JsonKey = ?JSON_DECODE(Val),
- Args#mrargs{start_key=JsonKey, end_key=JsonKey};
- "keys" ->
- Args#mrargs{keys=?JSON_DECODE(Val)};
- "startkey" ->
- Args#mrargs{start_key=?JSON_DECODE(Val)};
- "start_key" ->
- Args#mrargs{start_key=?JSON_DECODE(Val)};
- "startkey_docid" ->
- Args#mrargs{start_key_docid=list_to_binary(Val)};
- "start_key_doc_id" ->
- Args#mrargs{start_key_docid=list_to_binary(Val)};
- "endkey" ->
- Args#mrargs{end_key=?JSON_DECODE(Val)};
- "end_key" ->
- Args#mrargs{end_key=?JSON_DECODE(Val)};
- "endkey_docid" ->
- Args#mrargs{end_key_docid=list_to_binary(Val)};
- "end_key_doc_id" ->
- Args#mrargs{end_key_docid=list_to_binary(Val)};
- "limit" ->
- Args#mrargs{limit=parse_pos_int(Val)};
- "count" ->
- throw({query_parse_error, <<"QS param `count` is not `limit`">>});
- "stale" when Val == "ok" ->
- Args#mrargs{stale=ok};
- "stale" when Val == "update_after" ->
- Args#mrargs{stale=update_after};
- "stale" ->
- throw({query_parse_error, <<"Invalid value for `stale`.">>});
- "descending" ->
- case parse_boolean(Val) of
- true -> Args#mrargs{direction=rev};
- _ -> Args#mrargs{direction=fwd}
- end;
- "skip" ->
- Args#mrargs{skip=parse_pos_int(Val)};
- "group" ->
- case parse_boolean(Val) of
- true -> Args#mrargs{group_level=exact};
- _ -> Args#mrargs{group_level=0}
- end;
- "group_level" ->
- Args#mrargs{group_level=parse_pos_int(Val)};
- "inclusive_end" ->
- Args#mrargs{inclusive_end=parse_boolean(Val)};
- "include_docs" ->
- Args#mrargs{include_docs=parse_boolean(Val)};
- "attachments" ->
- case parse_boolean(Val) of
- true ->
- Opts = Args#mrargs.doc_options,
- Args#mrargs{doc_options=[attachments|Opts]};
- false ->
- Args
- end;
- "att_encoding_info" ->
- case parse_boolean(Val) of
- true ->
- Opts = Args#mrargs.doc_options,
- Args#mrargs{doc_options=[att_encoding_info|Opts]};
- false ->
- Args
- end;
- "update_seq" ->
- Args#mrargs{update_seq=parse_boolean(Val)};
- "conflicts" ->
- Args#mrargs{conflicts=parse_boolean(Val)};
- "list" ->
- Args#mrargs{list=list_to_binary(Val)};
- "callback" ->
- Args#mrargs{callback=list_to_binary(Val)};
- _ ->
- BKey = list_to_binary(Key),
- BVal = list_to_binary(Val),
- Args#mrargs{extra=[{BKey, BVal} | Args#mrargs.extra]}
- end.
-
-
-parse_boolean(true) ->
- true;
-parse_boolean(false) ->
- false;
-parse_boolean(Val) ->
- case string:to_lower(Val) of
- "true" -> true;
- "false" -> false;
- _ ->
- Msg = io_lib:format("Invalid boolean parameter: ~p", [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
-
-parse_int(Val) when is_integer(Val) ->
- Val;
-parse_int(Val) ->
- case (catch list_to_integer(Val)) of
- IntVal when is_integer(IntVal) ->
- IntVal;
- _ ->
- Msg = io_lib:format("Invalid value for integer: ~p", [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
-
-parse_pos_int(Val) ->
- case parse_int(Val) of
- IntVal when IntVal >= 0 ->
- IntVal;
- _ ->
- Fmt = "Invalid value for positive integer: ~p",
- Msg = io_lib:format(Fmt, [Val]),
- throw({query_parse_error, ?l2b(Msg)})
- end.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_index.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_index.erl b/apps/couch_mrview/src/couch_mrview_index.erl
deleted file mode 100644
index 5f5ae88..0000000
--- a/apps/couch_mrview/src/couch_mrview_index.erl
+++ /dev/null
@@ -1,180 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_index).
-
-
--export([get/2]).
--export([init/2, open/2, close/1, reset/1, delete/1]).
--export([start_update/3, purge/4, process_doc/3, finish_update/1, commit/1]).
--export([compact/3, swap_compacted/2]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
-
-get(Property, State) ->
- case Property of
- db_name ->
- State#mrst.db_name;
- idx_name ->
- State#mrst.idx_name;
- signature ->
- State#mrst.sig;
- update_seq ->
- State#mrst.update_seq;
- purge_seq ->
- State#mrst.purge_seq;
- update_options ->
- Opts = State#mrst.design_opts,
- IncDesign = couch_util:get_value(<<"include_design">>, Opts, false),
- LocalSeq = couch_util:get_value(<<"local_seq">>, Opts, false),
- SeqIndexed = couch_util:get_value(<<"seq_indexed">>, Opts, false),
- if IncDesign -> [include_design]; true -> [] end
- ++ if LocalSeq -> [local_seq]; true -> [] end
- ++ if SeqIndexed -> [seq_indexed]; true -> [] end;
- info ->
- #mrst{
- fd = Fd,
- sig = Sig,
- id_btree = IdBtree,
- log_btree = LogBtree,
- language = Lang,
- update_seq = UpdateSeq,
- purge_seq = PurgeSeq,
- views = Views,
- design_opts = Opts
- } = State,
- {ok, Size} = couch_file:bytes(Fd),
- {ok, DataSize} = couch_mrview_util:calculate_data_size(IdBtree,
- LogBtree,
- Views),
-
-
- IncDesign = couch_util:get_value(<<"include_design">>, Opts, false),
- LocalSeq = couch_util:get_value(<<"local_seq">>, Opts, false),
- SeqIndexed = couch_util:get_value(<<"seq_indexed">>, Opts, false),
- UpdateOptions =
- if IncDesign -> [<<"include_design">>]; true -> [] end
- ++ if LocalSeq -> [<<"local_seq">>]; true -> [] end
- ++ if SeqIndexed -> [<<"seq_indexed">>]; true -> [] end,
-
-
- {ok, [
- {signature, list_to_binary(couch_index_util:hexsig(Sig))},
- {language, Lang},
- {disk_size, Size},
- {data_size, DataSize},
- {update_seq, UpdateSeq},
- {purge_seq, PurgeSeq},
- {update_options, UpdateOptions}
- ]};
- Other ->
- throw({unknown_index_property, Other})
- end.
-
-
-init(Db, DDoc) ->
- couch_mrview_util:ddoc_to_mrst(couch_db:name(Db), DDoc).
-
-
-open(Db, State) ->
- #mrst{
- db_name=DbName,
- sig=Sig
- } = State,
- IndexFName = couch_mrview_util:index_file(DbName, Sig),
-
- % If we are upgrading from <=1.2.x, we upgrade the view
- % index file on the fly, avoiding an index reset.
- %
- % OldSig is `ok` if no upgrade happened.
- %
- % To remove suppport for 1.2.x auto-upgrades in the
- % future, just remove the next line and the code
- % between "upgrade code for <= 1.2.x" and
- % "end upgrade code for <= 1.2.x" and the corresponding
- % code in couch_mrview_util
-
- OldSig = couch_mrview_util:maybe_update_index_file(State),
-
- case couch_mrview_util:open_file(IndexFName) of
- {ok, Fd} ->
- case (catch couch_file:read_header(Fd)) of
- % upgrade code for <= 1.2.x
- {ok, {OldSig, Header}} ->
- % Matching view signatures.
- NewSt = couch_mrview_util:init_state(Db, Fd, State, Header),
- {ok, RefCounter} = couch_ref_counter:start([Fd]),
- {ok, NewSt#mrst{refc=RefCounter}};
- % end of upgrade code for <= 1.2.x
- {ok, {Sig, Header}} ->
- % Matching view signatures.
- NewSt = couch_mrview_util:init_state(Db, Fd, State, Header),
- {ok, RefCounter} = couch_ref_counter:start([Fd]),
- {ok, NewSt#mrst{refc=RefCounter}};
- _ ->
- NewSt = couch_mrview_util:reset_index(Db, Fd, State),
- {ok, RefCounter} = couch_ref_counter:start([Fd]),
- {ok, NewSt#mrst{refc=RefCounter}}
- end;
- {error, Reason} = Error ->
- ?LOG_ERROR("Failed to open view file '~s': ~s",
- [IndexFName, file:format_error(Reason)]),
- Error
- end.
-
-
-close(State) ->
- couch_file:close(State#mrst.fd).
-
-
-delete(#mrst{db_name=DbName, sig=Sig}=State) ->
- couch_file:close(State#mrst.fd),
- catch couch_mrview_util:delete_files(DbName, Sig).
-
-
-reset(State) ->
- couch_util:with_db(State#mrst.db_name, fun(Db) ->
- NewState = couch_mrview_util:reset_index(Db, State#mrst.fd, State),
- {ok, NewState}
- end).
-
-
-start_update(PartialDest, State, NumChanges) ->
- couch_mrview_updater:start_update(PartialDest, State, NumChanges).
-
-
-purge(Db, PurgeSeq, PurgedIdRevs, State) ->
- couch_mrview_updater:purge(Db, PurgeSeq, PurgedIdRevs, State).
-
-
-process_doc(Doc, Seq, State) ->
- couch_mrview_updater:process_doc(Doc, Seq, State).
-
-
-finish_update(State) ->
- couch_mrview_updater:finish_update(State).
-
-
-commit(State) ->
- Header = {State#mrst.sig, couch_mrview_util:make_header(State)},
- couch_file:write_header(State#mrst.fd, Header).
-
-
-compact(Db, State, Opts) ->
- couch_mrview_compactor:compact(Db, State, Opts).
-
-
-swap_compacted(OldState, NewState) ->
- couch_mrview_compactor:swap_compacted(OldState, NewState).
-
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_show.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_show.erl b/apps/couch_mrview/src/couch_mrview_show.erl
deleted file mode 100644
index 4ab8e17..0000000
--- a/apps/couch_mrview/src/couch_mrview_show.erl
+++ /dev/null
@@ -1,368 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_show).
-
--export([
- handle_doc_show_req/3,
- handle_doc_update_req/3,
- handle_view_list_req/3
-]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
--record(lacc, {
- db,
- req,
- resp,
- qserver,
- lname,
- etag,
- code,
- headers
-}).
-
-% /db/_design/foo/_show/bar/docid
-% show converts a json doc to a response of any content-type.
-% it looks up the doc an then passes it to the query server.
-% then it sends the response from the query server to the http client.
-
-maybe_open_doc(Db, DocId) ->
- case catch couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
- #doc{} = Doc -> Doc;
- {not_found, _} -> nil
- end.
-
-handle_doc_show_req(#httpd{
- path_parts=[_, _, _, _, ShowName, DocId]
- }=Req, Db, DDoc) ->
-
- % open the doc
- Doc = maybe_open_doc(Db, DocId),
-
- % we don't handle revs here b/c they are an internal api
- % returns 404 if there is no doc with DocId
- handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId);
-
-handle_doc_show_req(#httpd{
- path_parts=[_, _, _, _, ShowName, DocId|Rest]
- }=Req, Db, DDoc) ->
-
- DocParts = [DocId|Rest],
- DocId1 = ?l2b(string:join([?b2l(P)|| P <- DocParts], "/")),
-
- % open the doc
- Doc = maybe_open_doc(Db, DocId1),
-
- % we don't handle revs here b/c they are an internal api
- % pass 404 docs to the show function
- handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId1);
-
-handle_doc_show_req(#httpd{
- path_parts=[_, _, _, _, ShowName]
- }=Req, Db, DDoc) ->
- % with no docid the doc is nil
- handle_doc_show(Req, Db, DDoc, ShowName, nil);
-
-handle_doc_show_req(Req, _Db, _DDoc) ->
- couch_httpd:send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>).
-
-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) ->
- % get responder for ddoc/showname
- CurrentEtag = show_etag(Req, Doc, DDoc, []),
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- JsonReq = couch_httpd_external:json_req_obj(Req, Db, DocId),
- JsonDoc = couch_query_servers:json_doc(Doc),
- [<<"resp">>, ExternalResp] =
- couch_query_servers:ddoc_prompt(DDoc, [<<"shows">>, ShowName],
- [JsonDoc, JsonReq]),
- JsonResp = apply_etag(ExternalResp, CurrentEtag),
- couch_httpd_external:send_external_response(Req, JsonResp)
- end).
-
-
-show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) ->
- Accept = couch_httpd:header_value(Req, "Accept"),
- DocPart = case Doc of
- nil -> nil;
- Doc -> couch_httpd:doc_etag(Doc)
- end,
- couch_httpd:make_etag({couch_httpd:doc_etag(DDoc), DocPart, Accept,
- {UserCtx#user_ctx.name, UserCtx#user_ctx.roles}, More}).
-
-% updates a doc based on a request
-% handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db, _DDoc) ->
-% % anything but GET
-% send_method_not_allowed(Req, "POST,PUT,DELETE,ETC");
-
-% This call is creating a new doc using an _update function to
-% modify the provided request body.
-% /db/_design/foo/_update/bar
-handle_doc_update_req(#httpd{
- path_parts=[_, _, _, _, UpdateName]
- }=Req, Db, DDoc) ->
- send_doc_update_response(Req, Db, DDoc, UpdateName, nil, null);
-
-% /db/_design/foo/_update/bar/docid
-handle_doc_update_req(#httpd{
- path_parts=[_, _, _, _, UpdateName | DocIdParts]
- }=Req, Db, DDoc) ->
- DocId = ?l2b(string:join([?b2l(P) || P <- DocIdParts], "/")),
- Doc = maybe_open_doc(Db, DocId),
- send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId);
-
-
-handle_doc_update_req(Req, _Db, _DDoc) ->
- couch_httpd:send_error(Req, 404, <<"update_error">>, <<"Invalid path.">>).
-
-send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) ->
- JsonReq = couch_httpd_external:json_req_obj(Req, Db, DocId),
- JsonDoc = couch_query_servers:json_doc(Doc),
- Cmd = [<<"updates">>, UpdateName],
- UpdateResp = couch_query_servers:ddoc_prompt(DDoc, Cmd, [JsonDoc, JsonReq]),
- JsonResp = case UpdateResp of
- [<<"up">>, {NewJsonDoc}, {JsonResp0}] ->
- case couch_httpd:header_value(
- Req, "X-Couch-Full-Commit", "false") of
- "true" ->
- Options = [full_commit, {user_ctx, Req#httpd.user_ctx}];
- _ ->
- Options = [{user_ctx, Req#httpd.user_ctx}]
- end,
- NewDoc = couch_doc:from_json_obj({NewJsonDoc}),
- couch_doc:validate_docid(NewDoc#doc.id),
- {ok, NewRev} = couch_db:update_doc(Db, NewDoc, Options),
- NewRevStr = couch_doc:rev_to_str(NewRev),
- {[
- {<<"code">>, 201},
- {<<"headers">>, {[
- {<<"X-Couch-Update-NewRev">>, NewRevStr},
- {<<"X-Couch-Id">>, NewDoc#doc.id}
- ]}}
- | JsonResp0]};
- [<<"up">>, _Other, {JsonResp0}] ->
- {[{<<"code">>, 200} | JsonResp0]}
- end,
- % todo set location field
- couch_httpd_external:send_external_response(Req, JsonResp).
-
-
-handle_view_list_req(#httpd{method=Method}=Req, Db, DDoc)
- when Method =:= 'GET' orelse Method =:= 'OPTIONS' ->
- case Req#httpd.path_parts of
- [_, _, _DName, _, LName, VName] ->
- % Same design doc for view and list
- handle_view_list(Req, Db, DDoc, LName, DDoc, VName, undefined);
- [_, _, _, _, LName, DName, VName] ->
- % Different design docs for view and list
- VDocId = <<"_design/", DName/binary>>,
- {ok, VDDoc} = couch_db:open_doc(Db, VDocId, [ejson_body]),
- handle_view_list(Req, Db, DDoc, LName, VDDoc, VName, undefined);
- _ ->
- couch_httpd:send_error(Req, 404, <<"list_error">>, <<"Bad path.">>)
- end;
-handle_view_list_req(#httpd{method='POST'}=Req, Db, DDoc) ->
- {Props} = couch_httpd:json_body_obj(Req),
- Keys = proplists:get_value(<<"keys">>, Props),
- case Req#httpd.path_parts of
- [_, _, _DName, _, LName, VName] ->
- handle_view_list(Req, Db, DDoc, LName, DDoc, VName, Keys);
- [_, _, _, _, LName, DName, VName] ->
- % Different design docs for view and list
- VDocId = <<"_design/", DName/binary>>,
- {ok, VDDoc} = couch_db:open_doc(Db, VDocId, [ejson_body]),
- handle_view_list(Req, Db, DDoc, LName, VDDoc, VName, Keys);
- _ ->
- couch_httpd:send_error(Req, 404, <<"list_error">>, <<"Bad path.">>)
- end;
-handle_view_list_req(Req, _Db, _DDoc) ->
- couch_httpd:send_method_not_allowed(Req, "GET,POST,HEAD").
-
-
-handle_view_list(Req, Db, DDoc, LName, VDDoc, VName, Keys) ->
- Args0 = couch_mrview_http:parse_qs(Req, Keys),
- ETagFun = fun(BaseSig, Acc0) ->
- UserCtx = Req#httpd.user_ctx,
- Name = UserCtx#user_ctx.name,
- Roles = UserCtx#user_ctx.roles,
- Accept = couch_httpd:header_value(Req, "Accept"),
- Parts = {couch_httpd:doc_etag(DDoc), Accept, {Name, Roles}},
- ETag = couch_httpd:make_etag({BaseSig, Parts}),
- case couch_httpd:etag_match(Req, ETag) of
- true -> throw({etag_match, ETag});
- false -> {ok, Acc0#lacc{etag=ETag}}
- end
- end,
- Args = Args0#mrargs{preflight_fun=ETagFun},
- couch_httpd:etag_maybe(Req, fun() ->
- couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) ->
- Acc = #lacc{db=Db, req=Req, qserver=QServer, lname=LName},
- case VName of
- <<"_all_docs">> ->
- couch_mrview:query_all_docs(Db, Args, fun list_cb/2, Acc);
- _ ->
- couch_mrview:query_view(Db, VDDoc, VName, Args, fun list_cb/2, Acc)
- end
- end)
- end).
-
-
-list_cb({meta, Meta}, #lacc{code=undefined} = Acc) ->
- MetaProps = case couch_util:get_value(total, Meta) of
- undefined -> [];
- Total -> [{total_rows, Total}]
- end ++ case couch_util:get_value(offset, Meta) of
- undefined -> [];
- Offset -> [{offset, Offset}]
- end ++ case couch_util:get_value(update_seq, Meta) of
- undefined -> [];
- UpdateSeq -> [{update_seq, UpdateSeq}]
- end,
- start_list_resp({MetaProps}, Acc);
-list_cb({row, Row}, #lacc{code=undefined} = Acc) ->
- {ok, NewAcc} = start_list_resp({[]}, Acc),
- send_list_row(Row, NewAcc);
-list_cb({row, Row}, Acc) ->
- send_list_row(Row, Acc);
-list_cb(complete, Acc) ->
- #lacc{qserver = {Proc, _}, resp = Resp0} = Acc,
- if Resp0 =:= nil ->
- {ok, #lacc{resp = Resp}} = start_list_resp({[]}, Acc);
- true ->
- Resp = Resp0
- end,
- case couch_query_servers:proc_prompt(Proc, [<<"list_end">>]) of
- [<<"end">>, Data, Headers] ->
- Acc2 = fixup_headers(Headers, Acc#lacc{resp=Resp}),
- #lacc{resp = Resp2} = send_non_empty_chunk(Acc2, Data);
- [<<"end">>, Data] ->
- #lacc{resp = Resp2} = send_non_empty_chunk(Acc#lacc{resp=Resp}, Data)
- end,
- couch_httpd:last_chunk(Resp2),
- {ok, Resp2}.
-
-start_list_resp(Head, Acc) ->
- #lacc{db=Db, req=Req, qserver=QServer, lname=LName} = Acc,
- JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-
- [<<"start">>,Chunk,JsonResp] = couch_query_servers:ddoc_proc_prompt(QServer,
- [<<"lists">>, LName], [Head, JsonReq]),
- Acc2 = send_non_empty_chunk(fixup_headers(JsonResp, Acc), Chunk),
- {ok, Acc2}.
-
-fixup_headers(Headers, #lacc{etag=ETag} = Acc) ->
- Headers2 = apply_etag(Headers, ETag),
- #extern_resp_args{
- code = Code,
- ctype = CType,
- headers = ExtHeaders
- } = couch_httpd_external:parse_external_response(Headers2),
- Headers3 = couch_httpd_external:default_or_content_type(CType, ExtHeaders),
- Acc#lacc{code=Code, headers=Headers3}.
-
-send_list_row(Row, #lacc{qserver = {Proc, _}, resp = Resp} = Acc) ->
- RowObj = case couch_util:get_value(id, Row) of
- undefined -> [];
- Id -> [{id, Id}]
- end ++ case couch_util:get_value(key, Row) of
- undefined -> [];
- Key -> [{key, Key}]
- end ++ case couch_util:get_value(value, Row) of
- undefined -> [];
- Val -> [{value, Val}]
- end ++ case couch_util:get_value(doc, Row) of
- undefined -> [];
- Doc -> [{doc, Doc}]
- end,
- try couch_query_servers:proc_prompt(Proc, [<<"list_row">>, {RowObj}]) of
- [<<"chunks">>, Chunk, Headers] ->
- Acc2 = send_non_empty_chunk(fixup_headers(Headers, Acc), Chunk),
- {ok, Acc2};
- [<<"chunks">>, Chunk] ->
- Acc2 = send_non_empty_chunk(Acc, Chunk),
- {ok, Acc2};
- [<<"end">>, Chunk, Headers] ->
- Acc2 = send_non_empty_chunk(fixup_headers(Headers, Acc), Chunk),
- #lacc{resp = Resp2} = Acc2,
- couch_httpd:last_chunk(Resp2),
- {stop, Acc2};
- [<<"end">>, Chunk] ->
- Acc2 = send_non_empty_chunk(Acc, Chunk),
- #lacc{resp = Resp2} = Acc2,
- couch_httpd:last_chunk(Resp2),
- {stop, Acc2}
- catch Error ->
- case Resp of
- undefined ->
- {Code, _, _} = couch_httpd:error_info(Error),
- #lacc{req=Req, headers=Headers} = Acc,
- {ok, Resp2} = couch_httpd:start_chunked_response(Req, Code, Headers),
- Acc2 = Acc#lacc{resp=Resp2, code=Code};
- _ -> Resp2 = Resp, Acc2 = Acc
- end,
- couch_httpd:send_chunked_error(Resp2, Error),
- {stop, Acc2}
- end.
-
-send_non_empty_chunk(Acc, []) ->
- Acc;
-send_non_empty_chunk(#lacc{resp=undefined} = Acc, Chunk) ->
- #lacc{req=Req, code=Code, headers=Headers} = Acc,
- {ok, Resp} = couch_httpd:start_chunked_response(Req, Code, Headers),
- send_non_empty_chunk(Acc#lacc{resp = Resp}, Chunk);
-send_non_empty_chunk(#lacc{resp=Resp} = Acc, Chunk) ->
- couch_httpd:send_chunk(Resp, Chunk),
- Acc.
-
-
-apply_etag({ExternalResponse}, CurrentEtag) ->
- % Here we embark on the delicate task of replacing or creating the
- % headers on the JsonResponse object. We need to control the Etag and
- % Vary headers. If the external function controls the Etag, we'd have to
- % run it to check for a match, which sort of defeats the purpose.
- case couch_util:get_value(<<"headers">>, ExternalResponse, nil) of
- nil ->
- % no JSON headers
- % add our Etag and Vary headers to the response
- {[{<<"headers">>, {[{<<"Etag">>, CurrentEtag}, {<<"Vary">>, <<"Accept">>}]}} | ExternalResponse]};
- JsonHeaders ->
- {[case Field of
- {<<"headers">>, JsonHeaders} -> % add our headers
- JsonHeadersEtagged = json_apply_field({<<"Etag">>, CurrentEtag}, JsonHeaders),
- JsonHeadersVaried = json_apply_field({<<"Vary">>, <<"Accept">>}, JsonHeadersEtagged),
- {<<"headers">>, JsonHeadersVaried};
- _ -> % skip non-header fields
- Field
- end || Field <- ExternalResponse]}
- end.
-
-
-% Maybe this is in the proplists API
-% todo move to couch_util
-json_apply_field(H, {L}) ->
- json_apply_field(H, L, []).
-
-
-json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) ->
- % drop matching keys
- json_apply_field({Key, NewValue}, Headers, Acc);
-json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) ->
- % something else is next, leave it alone.
- json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]);
-json_apply_field({Key, NewValue}, [], Acc) ->
- % end of list, add ours
- {[{Key, NewValue}|Acc]}.
-
http://git-wip-us.apache.org/repos/asf/couchdb/blob/75150a18/apps/couch_mrview/src/couch_mrview_test_util.erl
----------------------------------------------------------------------
diff --git a/apps/couch_mrview/src/couch_mrview_test_util.erl b/apps/couch_mrview/src/couch_mrview_test_util.erl
deleted file mode 100644
index bcf8a87..0000000
--- a/apps/couch_mrview/src/couch_mrview_test_util.erl
+++ /dev/null
@@ -1,111 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_mrview_test_util).
-
--compile(export_all).
-
--include_lib("couch/include/couch_db.hrl").
-
--define(ADMIN, #user_ctx{roles=[<<"_admin">>]}).
-
-
-init_db(Name, Type) ->
- init_db(Name, Type, 10).
-
-
-init_db(Name, Type, Count) ->
- {ok, Db} = new_db(Name, Type),
- Docs = make_docs(Count),
- save_docs(Db, Docs).
-
-
-new_db(Name, Type) ->
- couch_server:delete(Name, [{user_ctx, ?ADMIN}]),
- {ok, Db} = couch_db:create(Name, [{user_ctx, ?ADMIN}]),
- save_docs(Db, [ddoc(Type)]).
-
-delete_db(Name) ->
- couch_server:delete(Name, [{user_ctx, ?ADMIN}]).
-
-save_docs(Db, Docs) ->
- {ok, _} = couch_db:update_docs(Db, Docs, []),
- couch_db:reopen(Db).
-
-
-make_docs(Count) ->
- [doc(I) || I <- lists:seq(1, Count)].
-
-ddoc(changes) ->
- couch_doc:from_json_obj({[
- {<<"_id">>, <<"_design/bar">>},
- {<<"options">>, {[
- {<<"seq_indexed">>, true}
- ]}},
- {<<"views">>, {[
- {<<"baz">>, {[
- {<<"map">>, <<"function(doc) {emit(doc.val, doc.val);}">>}
- ]}},
- {<<"bing">>, {[
- {<<"map">>, <<"function(doc) {}">>}
- ]}},
- {<<"zing">>, {[
- {<<"map">>, <<
- "function(doc) {\n"
- " if(doc.foo !== undefined)\n"
- " emit(doc.foo, 0);\n"
- "}"
- >>}
- ]}}
- ]}}
- ]});
-ddoc(map) ->
- couch_doc:from_json_obj({[
- {<<"_id">>, <<"_design/bar">>},
- {<<"views">>, {[
- {<<"baz">>, {[
- {<<"map">>, <<"function(doc) {emit(doc.val, doc.val);}">>}
- ]}},
- {<<"bing">>, {[
- {<<"map">>, <<"function(doc) {}">>}
- ]}},
- {<<"zing">>, {[
- {<<"map">>, <<
- "function(doc) {\n"
- " if(doc.foo !== undefined)\n"
- " emit(doc.foo, 0);\n"
- "}"
- >>}
- ]}}
- ]}}
- ]});
-ddoc(red) ->
- couch_doc:from_json_obj({[
- {<<"_id">>, <<"_design/bar">>},
- {<<"views">>, {[
- {<<"baz">>, {[
- {<<"map">>, <<
- "function(doc) {\n"
- " emit([doc.val % 2, doc.val], doc.val);\n"
- "}\n"
- >>},
- {<<"reduce">>, <<"function(keys, vals) {return sum(vals);}">>}
- ]}}
- ]}}
- ]}).
-
-
-doc(Id) ->
- couch_doc:from_json_obj({[
- {<<"_id">>, list_to_binary(integer_to_list(Id))},
- {<<"val">>, Id}
- ]}).