You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2018/08/29 13:06:31 UTC

[couchdb] branch user-partitioned-dbs-6 updated: add /_partition/partition/designdoc/ endpoints

This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch user-partitioned-dbs-6
in repository https://gitbox.apache.org/repos/asf/couchdb.git


The following commit(s) were added to refs/heads/user-partitioned-dbs-6 by this push:
     new a416244  add /_partition/partition/designdoc/ endpoints
a416244 is described below

commit a41624492d7a3de047c7b2ed7d55f4e058811cbd
Author: Garren Smith <ga...@gmail.com>
AuthorDate: Wed Aug 29 13:59:48 2018 +0100

    add /_partition/partition/designdoc/ endpoints
---
 src/chttpd/src/chttpd_db.erl               | 79 ++++++++++++++++++++++++---
 src/chttpd/src/chttpd_handlers.erl         | 10 +++-
 src/chttpd/src/chttpd_httpd_handlers.erl   |  6 ++-
 src/chttpd/src/chttpd_view.erl             | 17 +++++-
 src/couch_mrview/src/couch_mrview.erl      |  1 +
 src/couch_mrview/src/couch_mrview_util.erl | 14 +++++
 src/mango/src/mango_error.erl              |  7 +++
 src/mango/src/mango_httpd.erl              | 85 ++++++++++++++++++++++++++----
 src/mango/src/mango_httpd_handlers.erl     |  6 ++-
 9 files changed, 203 insertions(+), 22 deletions(-)

diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 06b3296..f03b480 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 9305632..f2098be 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 cb52e2c..2659d39 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 6b95706..627663c 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 7862afb..1b1a06b 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_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index 794b694..0fa5c1f 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 b2bbb39..603fb5f 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 2e87771..9a5c266 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);
@@ -179,9 +204,23 @@ 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 80e5e27..8589b7e 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.