rnewson closed pull request #1577: add /_partition/partition/designdoc/
endpoints
URL: https://github.com/apache/couchdb/pull/1577
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 06b329669f..f94b736862 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -18,7 +18,8 @@
db_req/2, couch_doc_open/4,handle_changes_req/2,
update_doc_result_to_json/1, update_doc_result_to_json/2,
handle_design_info_req/3, handle_view_cleanup_req/2,
- update_doc/4, http_code_from_status/1]).
+ update_doc/4, http_code_from_status/1,
+ handle_partition_req/2]).
-import(chttpd,
[send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
@@ -250,21 +251,83 @@ handle_view_cleanup_req(Req, Db) ->
ok = fabric:cleanup_index_files_all_nodes(Db),
send_json(Req, 202, {[{ok, true}]}).
+
+handle_partition_req(#httpd{
+ path_parts=[DbName, <<"_partition">>, Partition, _Design, Name,
<<"_",_/binary>> = Action | _Rest]
+ }=Req, Db) ->
+
+ validate_partition_req(Req, Partition, DbName),
+ DDoc = get_design_doc(DbName, Name),
+ Partitioned = couch_mrview:get_partitioned_opt(DDoc#doc.body, true),
+
+ case Partitioned of
+ true ->
+ Handler = chttpd_handlers:partition_design_handler(Action, fun
bad_action_partition_design_req/4),
+ Handler(Req, Db, DDoc, Partition);
+ false ->
+ throw({bad_request, <<"partition query is not supported in this
design doc.">>})
+ end;
+
+handle_partition_req(#httpd{
+ path_parts=[DbName, <<"_partition">>, Partition, Action | _Rest]
+ }=Req, Db) ->
+ validate_partition_req(Req, Partition, DbName),
+ Handler = chttpd_handlers:partition_handler(Action, fun
bad_action_partition_req/3),
+ Handler(Req, Db, Partition);
+
+handle_partition_req(_Req, _Db) ->
+ throw({bad_request, <<"missing partition key">>}).
+
+
+bad_action_partition_design_req(Req, _Db, _DDoc, _PartitionKey) ->
+ chttpd:send_error(Req, 404, <<"partition_error">>, <<"Invalid path.">>).
+
+
+bad_action_partition_req(Req, _Db, _PartitionKey) ->
+ chttpd:send_error(Req, 404, <<"partition_error">>, <<"Invalid path.">>).
+
+
+validate_partition_req(Req, Partition, DbName) ->
+ couch_doc:validate_docid(Partition, DbName),
+ validate_no_partition_in_qs(Req),
+
+ case mem3:is_partitioned(DbName) of
+ false -> throw({bad_request, <<"Database is not partitioned">>});
+ true -> ok
+ end.
+
+
handle_design_req(#httpd{
path_parts=[_DbName, _Design, Name, <<"_",_/binary>> = Action | _Rest]
}=Req, Db) ->
+ validate_no_partition_in_qs(Req),
DbName = mem3:dbname(couch_db:name(Db)),
- case ddoc_cache:open(DbName, <<"_design/", Name/binary>>) of
- {ok, DDoc} ->
- Handler = chttpd_handlers:design_handler(Action, fun bad_action_req/3),
- Handler(Req, Db, DDoc);
- Error ->
- throw(Error)
- end;
+ DDoc = get_design_doc(DbName, Name),
+ Handler = chttpd_handlers:design_handler(Action, fun bad_action_req/3),
+ Handler(Req, Db, DDoc);
handle_design_req(Req, Db) ->
db_req(Req, Db).
+
+get_design_doc(DbName, Name) ->
+ case ddoc_cache:open(DbName, <<"_design/", Name/binary>>) of
+ {ok, DDoc} ->
+ DDoc;
+ Error ->
+ throw(Error)
+ end.
+
+
+validate_no_partition_in_qs(Req) ->
+ case chttpd:qs_value(Req, "partition") of
+ undefined ->
+ ok;
+ _ ->
+ throw({bad_request, <<"Partition is not allowed in the query
string">>})
+ end.
+
+
bad_action_req(#httpd{path_parts=[_, _, Name|FileNameParts]}=Req, Db, _DDoc) ->
db_attachment_req(Req, Db, <<"_design/",Name/binary>>, FileNameParts).
diff --git a/src/chttpd/src/chttpd_handlers.erl
b/src/chttpd/src/chttpd_handlers.erl
index 930563230e..f2098bef22 100644
--- a/src/chttpd/src/chttpd_handlers.erl
+++ b/src/chttpd/src/chttpd_handlers.erl
@@ -15,7 +15,9 @@
-export([
url_handler/2,
db_handler/2,
- design_handler/2
+ design_handler/2,
+ partition_handler/2,
+ partition_design_handler/2
]).
-define(SERVICE_ID, chttpd_handlers).
@@ -35,6 +37,12 @@ db_handler(HandlerKey, DefaultFun) ->
design_handler(HandlerKey, DefaultFun) ->
select(collect(design_handler, [HandlerKey]), DefaultFun).
+partition_handler(HandlerKey, DefaultFun) ->
+ select(collect(partition_handler, [HandlerKey]), DefaultFun).
+
+partition_design_handler(HandlerKey, DefaultFun) ->
+ select(collect(partition_design_handler, [HandlerKey]), DefaultFun).
+
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
diff --git a/src/chttpd/src/chttpd_httpd_handlers.erl
b/src/chttpd/src/chttpd_httpd_handlers.erl
index cb52e2c40d..2659d39f50 100644
--- a/src/chttpd/src/chttpd_httpd_handlers.erl
+++ b/src/chttpd/src/chttpd_httpd_handlers.erl
@@ -12,7 +12,7 @@
-module(chttpd_httpd_handlers).
--export([url_handler/1, db_handler/1, design_handler/1]).
+-export([url_handler/1, db_handler/1, design_handler/1,
partition_design_handler/1]).
url_handler(<<>>) -> fun chttpd_misc:handle_welcome_req/1;
url_handler(<<"favicon.ico">>) -> fun chttpd_misc:handle_favicon_req/1;
@@ -32,6 +32,7 @@ url_handler(_) -> no_match.
db_handler(<<"_view_cleanup">>) -> fun chttpd_db:handle_view_cleanup_req/2;
db_handler(<<"_compact">>) -> fun chttpd_db:handle_compact_req/2;
db_handler(<<"_design">>) -> fun chttpd_db:handle_design_req/2;
+db_handler(<<"_partition">>) -> fun chttpd_db:handle_partition_req/2;
db_handler(<<"_temp_view">>) -> fun chttpd_view:handle_temp_view_req/2;
db_handler(<<"_changes">>) -> fun chttpd_db:handle_changes_req/2;
db_handler(_) -> no_match.
@@ -43,3 +44,6 @@ design_handler(<<"_update">>) -> fun
chttpd_show:handle_doc_update_req/3;
design_handler(<<"_info">>) -> fun chttpd_db:handle_design_info_req/3;
design_handler(<<"_rewrite">>) -> fun chttpd_rewrite:handle_rewrite_req/3;
design_handler(_) -> no_match.
+
+partition_design_handler(<<"_view">>) -> fun
chttpd_view:handle_partition_view_req/4;
+partition_design_handler(_) -> no_match.
diff --git a/src/chttpd/src/chttpd_view.erl b/src/chttpd/src/chttpd_view.erl
index 6b9570656d..627663cbc7 100644
--- a/src/chttpd/src/chttpd_view.erl
+++ b/src/chttpd/src/chttpd_view.erl
@@ -14,7 +14,7 @@
-include_lib("couch/include/couch_db.hrl").
-include_lib("couch_mrview/include/couch_mrview.hrl").
--export([handle_view_req/3, handle_temp_view_req/2]).
+-export([handle_view_req/3, handle_temp_view_req/2,
handle_partition_view_req/4]).
multi_query_view(Req, Db, DDoc, ViewName, Queries) ->
Args0 = couch_mrview_http:parse_params(Req, undefined),
@@ -42,6 +42,10 @@ multi_query_view(Req, Db, DDoc, ViewName, Queries) ->
design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
Args = couch_mrview_http:parse_params(Req, Keys),
+ design_doc_view_int(Req, Db, DDoc, ViewName, Args).
+
+
+design_doc_view_int(Req, Db, DDoc, ViewName, Args) ->
couch_mrview_util:validate_args(Args, [view]),
Max = chttpd:chunked_response_buffer_size(),
VAcc = #vacc{db=Db, req=Req, threshold=Max},
@@ -102,6 +106,17 @@ handle_temp_view_req(Req, _Db) ->
chttpd:send_error(Req, 410, gone, Msg).
+handle_partition_view_req(#httpd{method='GET',
+ path_parts=[_, _, _, _, _, _, ViewName]} = Req, Db, DDoc, Partition) ->
+ Keys = chttpd:qs_json_value(Req, "keys", undefined),
+ Args = couch_mrview_http:parse_params(Req, Keys),
+ Args1 = couch_mrview_util:set_extra(Args, partition, Partition),
+ Args2 = couch_mrview_util:set_extra(Args1, partitioned, true),
+ design_doc_view_int(Req, Db, DDoc, ViewName, Args2);
+
+handle_partition_view_req(Req, _Db, _DDoc, _Pk) ->
+ chttpd:send_method_not_allowed(Req, "GET").
+
-ifdef(TEST).
diff --git a/src/couch_mrview/src/couch_mrview.erl
b/src/couch_mrview/src/couch_mrview.erl
index 7862afb1e6..1b1a06b743 100644
--- a/src/couch_mrview/src/couch_mrview.erl
+++ b/src/couch_mrview/src/couch_mrview.erl
@@ -24,6 +24,7 @@
-export([refresh/2]).
-export([compact/2, compact/3, cancel_compaction/2]).
-export([cleanup/1]).
+-export([get_partitioned_opt/2]).
-include_lib("couch/include/couch_db.hrl").
-include_lib("couch_mrview/include/couch_mrview.hrl").
diff --git a/src/couch_mrview/src/couch_mrview_http.erl
b/src/couch_mrview/src/couch_mrview_http.erl
index 86df7968af..004caef09f 100644
--- a/src/couch_mrview/src/couch_mrview_http.erl
+++ b/src/couch_mrview/src/couch_mrview_http.erl
@@ -582,9 +582,6 @@ parse_param(Key, Val, Args, IsDecoded) ->
Args#mrargs{callback=couch_util:to_binary(Val)};
"sorted" ->
Args#mrargs{sorted=parse_boolean(Val)};
- "partition" ->
- Partition = couch_util:to_binary(Val),
- couch_mrview_util:set_extra(Args, partition, Partition);
_ ->
BKey = couch_util:to_binary(Key),
BVal = couch_util:to_binary(Val),
diff --git a/src/couch_mrview/src/couch_mrview_util.erl
b/src/couch_mrview/src/couch_mrview_util.erl
index 794b694820..0fa5c1faa8 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -617,6 +617,20 @@ validate_args(Args, ValidateOptions) ->
ok
end,
+ case {Partitioned, Args#mrargs.conflicts} of
+ {true, true} ->
+ mrverror(<<"`conflicts=true` is not supported in this view.">>);
+ {_, _} ->
+ ok
+ end,
+
+ case {Partitioned, Args#mrargs.stable} of
+ {true, true} ->
+ mrverror(<<"`stable=true` is not supported in this view.">>);
+ {_, _} ->
+ ok
+ end,
+
Args1 = case {Style, Partitioned, Partition} of
{all_docs, true, undefined} ->
Args;
diff --git a/src/mango/src/mango_error.erl b/src/mango/src/mango_error.erl
index b2bbb392a8..603fb5fd44 100644
--- a/src/mango/src/mango_error.erl
+++ b/src/mango/src/mango_error.erl
@@ -73,6 +73,13 @@ info(mango_fields, {invalid_field_json, BadField}) ->
fmt("Invalid JSON for field spec: ~w", [BadField])
};
+info(mango_httpd, partition_field_error) ->
+ {
+ 400,
+ <<"bad request">>,
+ <<"`partition` is not a valid parameter.">>
+ };
+
info(mango_httpd, error_saving_ddoc) ->
{
500,
diff --git a/src/mango/src/mango_httpd.erl b/src/mango/src/mango_httpd.erl
index 2e8777135d..1fb1527524 100644
--- a/src/mango/src/mango_httpd.erl
+++ b/src/mango/src/mango_httpd.erl
@@ -14,7 +14,8 @@
-export([
- handle_req/2
+ handle_req/2,
+ handle_partition_req/3
]).
@@ -38,13 +39,7 @@ handle_req(#httpd{} = Req, Db0) ->
handle_req_int(Req, Db)
catch
throw:{mango_error, Module, Reason} ->
- case mango_error:info(Module, Reason) of
- {500, ErrorStr, ReasonStr} ->
- Stack = erlang:get_stacktrace(),
- chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack});
- {Code, ErrorStr, ReasonStr} ->
- chttpd:send_error(Req, Code, ErrorStr, ReasonStr)
- end
+ handle_req_error(Req, Module, Reason)
end.
@@ -58,6 +53,34 @@ handle_req_int(_, _) ->
throw({not_found, missing}).
+handle_partition_req(#httpd{} = Req, Db0, Partition) ->
+ try
+ Db = set_user_ctx(Req, Db0),
+ handle_partition_req_int(Req, Db, Partition)
+ catch
+ throw:{mango_error, Module, Reason} ->
+ handle_req_error(Req, Module, Reason)
+ end.
+
+
+handle_partition_req_int(#httpd{path_parts=[_, _, _, <<"_explain">> | _]} =
Req, Db, Partition) ->
+ handle_partition_explain_req(Req, Db, Partition);
+handle_partition_req_int(#httpd{path_parts=[_, _, _,<<"_find">> | _]} = Req,
Db, Partition) ->
+ handle_partition_find_req(Req, Db, Partition);
+handle_partition_req_int(_, _, _) ->
+ throw({not_found, missing}).
+
+
+handle_req_error(Req, Module, Reason) ->
+ case mango_error:info(Module, Reason) of
+ {500, ErrorStr, ReasonStr} ->
+ Stack = erlang:get_stacktrace(),
+ chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack});
+ {Code, ErrorStr, ReasonStr} ->
+ chttpd:send_error(Req, Code, ErrorStr, ReasonStr)
+ end.
+
+
handle_index_req(#httpd{method='GET', path_parts=[_, _]}=Req, Db) ->
Params = lists:flatmap(fun({K, V}) -> parse_index_param(K, V) end,
chttpd:qs(Req)),
@@ -170,7 +193,9 @@ handle_index_req(#httpd{path_parts=[_, _, _DDocId0, _Type,
_Name]}=Req, _Db) ->
handle_explain_req(#httpd{method='POST'}=Req, Db) ->
chttpd:validate_ctype(Req, "application/json"),
- {ok, Opts0} = mango_opts:validate_find(chttpd:json_body_obj(Req)),
+ {Body0} = chttpd:json_body_obj(Req),
+ check_for_partition_param(Body0),
+ {ok, Opts0} = mango_opts:validate_find({Body0}),
{value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0),
Resp = mango_crud:explain(Db, Sel, Opts),
chttpd:send_json(Req, Resp);
@@ -178,10 +203,24 @@ handle_explain_req(#httpd{method='POST'}=Req, Db) ->
handle_explain_req(Req, _Db) ->
chttpd:send_method_not_allowed(Req, "POST").
+
+handle_partition_explain_req(#httpd{method='POST'}=Req, Db, Partition) ->
+ chttpd:validate_ctype(Req, "application/json"),
+ {ok, Body} = add_partition_to_query(Req, Partition),
+ {ok, Opts0} = mango_opts:validate_find(Body),
+ {value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0),
+ Resp = mango_crud:explain(Db, Sel, Opts),
+ chttpd:send_json(Req, Resp);
+
+handle_partition_explain_req(Req, _Db, _Partition) ->
+ chttpd:send_method_not_allowed(Req, "POST").
+
handle_find_req(#httpd{method='POST'}=Req, Db) ->
chttpd:validate_ctype(Req, "application/json"),
- {ok, Opts0} = mango_opts:validate_find(chttpd:json_body_obj(Req)),
+ {Body0} = chttpd:json_body_obj(Req),
+ check_for_partition_param(Body0),
+ {ok, Opts0} = mango_opts:validate_find({Body0}),
{value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0),
{ok, Resp0} = start_find_resp(Req),
{ok, AccOut} = run_find(Resp0, Db, Sel, Opts),
@@ -191,6 +230,32 @@ handle_find_req(Req, _Db) ->
chttpd:send_method_not_allowed(Req, "POST").
+handle_partition_find_req(#httpd{method='POST'}=Req, Db, Partition) ->
+ chttpd:validate_ctype(Req, "application/json"),
+ {ok, Body} = add_partition_to_query(Req, Partition),
+ {ok, Opts0} = mango_opts:validate_find(Body),
+ {value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0),
+ {ok, Resp0} = start_find_resp(Req),
+ {ok, AccOut} = run_find(Resp0, Db, Sel, Opts),
+ end_find_resp(AccOut);
+
+handle_partition_find_req(Req, _Db, _Partition) ->
+ chttpd:send_method_not_allowed(Req, "POST").
+
+check_for_partition_param(Body) ->
+ case lists:keyfind(<<"partition">>, 1, Body) of
+ false -> ok;
+ _ -> ?MANGO_ERROR(partition_field_error)
+ end.
+
+
+add_partition_to_query(Req, Partition) ->
+ {Body0} = chttpd:json_body_obj(Req),
+ check_for_partition_param(Body0),
+ Body1 = [{<<"partition">>, Partition} | Body0],
+ {ok, {Body1}}.
+
+
set_user_ctx(#httpd{user_ctx=Ctx}, Db) ->
{ok, NewDb} = couch_db:set_user_ctx(Db, Ctx),
NewDb.
diff --git a/src/mango/src/mango_httpd_handlers.erl
b/src/mango/src/mango_httpd_handlers.erl
index 80e5e277e2..8589b7e149 100644
--- a/src/mango/src/mango_httpd_handlers.erl
+++ b/src/mango/src/mango_httpd_handlers.erl
@@ -12,7 +12,7 @@
-module(mango_httpd_handlers).
--export([url_handler/1, db_handler/1, design_handler/1]).
+-export([url_handler/1, db_handler/1, design_handler/1, partition_handler/1]).
url_handler(_) -> no_match.
@@ -22,3 +22,7 @@ db_handler(<<"_find">>) -> fun
mango_httpd:handle_req/2;
db_handler(_) -> no_match.
design_handler(_) -> no_match.
+
+partition_handler(<<"_find">>) -> fun mango_httpd:handle_partition_req/3;
+partition_handler(<<"_explain">>) -> fun mango_httpd:handle_partition_req/3;
+partition_handler(_) -> no_match.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services