summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/couchdb/default.ini.tpl.in1
-rw-r--r--share/www/script/test/list_views.js31
-rw-r--r--share/www/script/test/show_documents.js13
-rw-r--r--src/couchdb/couch_db.hrl3
-rw-r--r--src/couchdb/couch_httpd_db.erl89
-rw-r--r--src/couchdb/couch_httpd_show.erl56
-rw-r--r--src/couchdb/couch_httpd_view.erl49
7 files changed, 174 insertions, 68 deletions
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index c4a87363..77a09fcb 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -68,6 +68,7 @@ _whoami = {couch_httpd_misc_handlers, handle_whoami_req}
_view_cleanup = {couch_httpd_db, handle_view_cleanup_req}
_compact = {couch_httpd_db, handle_compact_req}
_design = {couch_httpd_db, handle_design_req}
+_view = {couch_httpd_view, handle_db_view_req}
_temp_view = {couch_httpd_view, handle_temp_view_req}
_changes = {couch_httpd_db, handle_changes_req}
diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js
index f9268479..67043ac8 100644
--- a/share/www/script/test/list_views.js
+++ b/share/www/script/test/list_views.js
@@ -176,6 +176,10 @@ couchTests.list_views = function(debug) {
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
T(xhr.status == 200, "standard get should be 200");
T(/head0123456789tail/.test(xhr.responseText));
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=basicBasic");
+ T(xhr.status == 200, "standard get should be 200");
+ T(/head0123456789tail/.test(xhr.responseText));
// test that etags are available
var etag = xhr.getResponseHeader("etag");
@@ -183,6 +187,12 @@ couchTests.list_views = function(debug) {
headers: {"if-none-match": etag}
});
T(xhr.status == 304);
+
+ var etag = xhr.getResponseHeader("etag");
+ xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=basicBasic", {
+ headers: {"if-none-match": etag}
+ });
+ T(xhr.status == 304);
// test the richness of the arguments
xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView");
@@ -208,17 +218,28 @@ couchTests.list_views = function(debug) {
T(resp.req.cookie);
// get with query params
- var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3");
+ xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3");
T(xhr.status == 200, "with query params");
T(/Total Rows/.test(xhr.responseText));
T(!(/Key: 1/.test(xhr.responseText)));
T(/FirstKey: 3/.test(xhr.responseText));
T(/LastKey: 9/.test(xhr.responseText));
-
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=simpleForm&startkey=3");
+ T(xhr.status == 200, "with query params");
+ T(/Total Rows/.test(xhr.responseText));
+ T(!(/Key: 1/.test(xhr.responseText)));
+ T(/FirstKey: 3/.test(xhr.responseText));
+ T(/LastKey: 9/.test(xhr.responseText));
+
// with 0 rows
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30");
T(xhr.status == 200, "0 rows");
T(/Total Rows/.test(xhr.responseText));
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/basicView?list=simpleForm&startkey=30");
+ T(xhr.status == 200, "0 rows");
+ T(/Total Rows/.test(xhr.responseText));
//too many Get Rows
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/tooManyGetRows/basicView");
@@ -231,6 +252,12 @@ couchTests.list_views = function(debug) {
T(xhr.status == 200, "reduce 0 rows");
T(/Total Rows/.test(xhr.responseText));
T(/LastKey: undefined/.test(xhr.responseText));
+
+ // reduce with 0 rows
+ var xhr = CouchDB.request("GET", "/test_suite_db/_view/lists/withReduce?list=simpleForm&startkey=30");
+ T(xhr.status == 200, "reduce 0 rows");
+ T(/Total Rows/.test(xhr.responseText));
+ T(/LastKey: undefined/.test(xhr.responseText));
// when there is a reduce present, but not used
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false");
diff --git a/share/www/script/test/show_documents.js b/share/www/script/test/show_documents.js
index 7201ae81..6f41f6e8 100644
--- a/share/www/script/test/show_documents.js
+++ b/share/www/script/test/show_documents.js
@@ -147,6 +147,14 @@ couchTests.show_documents = function(debug) {
// Fix for COUCHDB-379
T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB"));
+
+
+ xhr = CouchDB.request("GET", "/test_suite_db/"+docid+"?show=template/hello");
+ T(xhr.responseText == "Hello World");
+ T(/charset=utf-8/.test(xhr.getResponseHeader("Content-Type")))
+
+ // Fix for COUCHDB-379
+ T(equals(xhr.getResponseHeader("Server").substr(0,7), "CouchDB"));
// // error stacktraces
// xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/render-error/"+docid);
@@ -163,7 +171,10 @@ couchTests.show_documents = function(debug) {
// show with doc
xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/"+docid);
T(xhr.responseText == "Just Rusty");
-
+
+ xhr = CouchDB.request("GET", "/test_suite_db/"+docid+"?show=template/just-name");
+ T(xhr.responseText == "Just Rusty");
+
// show with missing doc
xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/just-name/missingdoc");
diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
index abb301eb..f74d0ae0 100644
--- a/src/couchdb/couch_db.hrl
+++ b/src/couchdb/couch_db.hrl
@@ -167,7 +167,8 @@
include_docs = false,
stale = false,
multi_get = false,
- callback = nil
+ callback = nil,
+ list = nil
}).
-record(view_fold_helper_funs, {
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index f5bebc20..fcbdc4c6 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -26,7 +26,8 @@
-record(doc_query_args, {
options = [],
rev = nil,
- open_revs = []
+ open_revs = [],
+ show = nil
}).
% Database request handlers
@@ -192,7 +193,6 @@ handle_design_info_req(#httpd{
handle_design_info_req(Req, _Db) ->
send_method_not_allowed(Req, "GET").
-
create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
ok = couch_httpd:verify_is_server_admin(Req),
case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
@@ -558,8 +558,6 @@ all_docs_view(Req, Db, Keys) ->
end
end).
-
-
db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) ->
% check for the existence of the doc to handle the 404 case.
couch_doc_open(Db, DocId, nil, []),
@@ -572,43 +570,50 @@ db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) ->
db_doc_req(#httpd{method='GET'}=Req, Db, DocId) ->
#doc_query_args{
+ show = Format,
rev = Rev,
open_revs = Revs,
options = Options
} = parse_doc_query(Req),
- case Revs of
- [] ->
- Doc = couch_doc_open(Db, DocId, Rev, Options),
- DiskEtag = couch_httpd:doc_etag(Doc),
- couch_httpd:etag_respond(Req, DiskEtag, fun() ->
- Headers = case Doc#doc.meta of
- [] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta
- _ -> []
- end,
- send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
- end);
- _ ->
- {ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options),
- {ok, Resp} = start_json_response(Req, 200),
- send_chunk(Resp, "["),
- % We loop through the docs. The first time through the separator
- % is whitespace, then a comma on subsequent iterations.
- lists:foldl(
- fun(Result, AccSeparator) ->
- case Result of
- {ok, Doc} ->
- JsonDoc = couch_doc:to_json_obj(Doc, Options),
- Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
- send_chunk(Resp, AccSeparator ++ Json);
- {{not_found, missing}, RevId} ->
- Json = ?JSON_ENCODE({[{"missing", RevId}]}),
- send_chunk(Resp, AccSeparator ++ Json)
+ case Format of
+ nil ->
+ case Revs of
+ [] ->
+ Doc = couch_doc_open(Db, DocId, Rev, Options),
+ DiskEtag = couch_httpd:doc_etag(Doc),
+ couch_httpd:etag_respond(Req, DiskEtag, fun() ->
+ Headers = case Doc#doc.meta of
+ [] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta
+ _ -> []
end,
- "," % AccSeparator now has a comma
- end,
- "", Results),
- send_chunk(Resp, "]"),
- end_json_response(Resp)
+ send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
+ end);
+ _ ->
+ {ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options),
+ {ok, Resp} = start_json_response(Req, 200),
+ send_chunk(Resp, "["),
+ % We loop through the docs. The first time through the separator
+ % is whitespace, then a comma on subsequent iterations.
+ lists:foldl(
+ fun(Result, AccSeparator) ->
+ case Result of
+ {ok, Doc} ->
+ JsonDoc = couch_doc:to_json_obj(Doc, Options),
+ Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
+ send_chunk(Resp, AccSeparator ++ Json);
+ {{not_found, missing}, RevId} ->
+ Json = ?JSON_ENCODE({[{"missing", RevId}]}),
+ send_chunk(Resp, AccSeparator ++ Json)
+ end,
+ "," % AccSeparator now has a comma
+ end,
+ "", Results),
+ send_chunk(Resp, "]"),
+ end_json_response(Resp)
+ end;
+ _ ->
+ {DesignName, ShowName} = Format,
+ couch_httpd_show:handle_doc_show(Req, DesignName, ShowName, DocId, Db)
end;
db_doc_req(#httpd{method='POST'}=Req, Db, DocId) ->
@@ -843,6 +848,16 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts)
db_attachment_req(Req, _Db, _DocId, _FileNameParts) ->
send_method_not_allowed(Req, "DELETE,GET,HEAD,PUT").
+parse_doc_format(FormatStr) when is_binary(FormatStr) ->
+ parse_doc_format(?b2l(FormatStr));
+parse_doc_format(FormatStr) when is_list(FormatStr) ->
+ SplitFormat = lists:splitwith(fun($/) -> false; (_) -> true end, FormatStr),
+ case SplitFormat of
+ {DesignName, [$/ | ShowName]} -> {?l2b(DesignName), ?l2b(ShowName)};
+ _Else -> throw({bad_request, <<"Invalid doc format">>})
+ end;
+parse_doc_format(_BadFormatStr) ->
+ throw({bad_request, <<"Invalid doc format">>}).
parse_doc_query(Req) ->
lists:foldl(fun({Key,Value}, Args) ->
@@ -875,6 +890,8 @@ parse_doc_query(Req) ->
{"open_revs", RevsJsonStr} ->
JsonArray = ?JSON_DECODE(RevsJsonStr),
Args#doc_query_args{open_revs=[couch_doc:parse_rev(Rev) || Rev <- JsonArray]};
+ {"show", FormatStr} ->
+ Args#doc_query_args{show=parse_doc_format(FormatStr)};
_Else -> % unknown key value pair, ignore.
Args
end
diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl
index c29d89c5..9c873e8f 100644
--- a/src/couchdb/couch_httpd_show.erl
+++ b/src/couchdb/couch_httpd_show.erl
@@ -12,7 +12,8 @@
-module(couch_httpd_show).
--export([handle_doc_show_req/2, handle_view_list_req/2]).
+-export([handle_doc_show_req/2, handle_view_list_req/2,
+ handle_doc_show/5, handle_view_list/6]).
-include("couch_db.hrl").
@@ -26,58 +27,59 @@ handle_doc_show_req(#httpd{
method='GET',
path_parts=[_DbName, _Design, DesignName, _Show, ShowName, DocId]
}=Req, Db) ->
- DesignId = <<"_design/", DesignName/binary>>,
- #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
- Doc = try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
- FoundDoc -> FoundDoc
- catch
- _ -> nil
- end,
- send_doc_show_response(Lang, ShowSrc, DocId, Doc, Req, Db);
-
+ handle_doc_show(Req, DesignName, ShowName, DocId, Db);
+
handle_doc_show_req(#httpd{
method='GET',
path_parts=[_DbName, _Design, DesignName, _Show, ShowName]
}=Req, Db) ->
- DesignId = <<"_design/", DesignName/binary>>,
- #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
- send_doc_show_response(Lang, ShowSrc, nil, nil, Req, Db);
+ handle_doc_show(Req, DesignName, ShowName, nil, Db);
handle_doc_show_req(#httpd{method='GET'}=Req, _Db) ->
send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>);
handle_doc_show_req(Req, _Db) ->
send_method_not_allowed(Req, "GET,HEAD").
-
-handle_view_list_req(#httpd{method='GET',
- path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
+
+handle_doc_show(Req, DesignName, ShowName, DocId, Db) ->
DesignId = <<"_design/", DesignName/binary>>,
#doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]),
- send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, nil);
+ ShowSrc = get_nested_json_value({Props}, [<<"shows">>, ShowName]),
+ Doc = case DocId of
+ nil -> nil;
+ _ ->
+ try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
+ FoundDoc -> FoundDoc
+ catch
+ _ -> nil
+ end
+ end,
+ send_doc_show_response(Lang, ShowSrc, DocId, Doc, Req, Db).
+
+handle_view_list_req(#httpd{method='GET',
+ path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
+ handle_view_list(Req, DesignName, ListName, ViewName, Db, nil);
handle_view_list_req(#httpd{method='GET'}=Req, _Db) ->
send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>);
handle_view_list_req(#httpd{method='POST',
path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
- DesignId = <<"_design/", DesignName/binary>>,
- #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]),
ReqBody = couch_httpd:body(Req),
{Props2} = ?JSON_DECODE(ReqBody),
Keys = proplists:get_value(<<"keys">>, Props2, nil),
- send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req#httpd{req_body=ReqBody}, Db, Keys);
+ handle_view_list(Req#httpd{req_body=ReqBody}, DesignName, ListName, ViewName, Db, Keys);
handle_view_list_req(Req, _Db) ->
send_method_not_allowed(Req, "GET,POST,HEAD").
+handle_view_list(Req, DesignName, ListName, ViewName, Db, Keys) ->
+ DesignId = <<"_design/", DesignName/binary>>,
+ #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
+ Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
+ ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]),
+ send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys).
get_nested_json_value({Props}, [Key|Keys]) ->
case proplists:get_value(Key, Props, nil) of
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 2028840c..0150582a 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -13,7 +13,7 @@
-module(couch_httpd_view).
-include("couch_db.hrl").
--export([handle_view_req/2,handle_temp_view_req/2]).
+-export([handle_view_req/2,handle_temp_view_req/2,handle_db_view_req/2]).
-export([get_stale_type/1, get_reduce_type/1, parse_view_params/3]).
-export([make_view_fold_fun/6, finish_view_fold/3, view_row_obj/3]).
@@ -73,6 +73,49 @@ handle_view_req(#httpd{method='POST',
handle_view_req(Req, _Db) ->
send_method_not_allowed(Req, "GET,POST,HEAD").
+handle_db_view_req(#httpd{method='GET',
+ path_parts=[_Db, _View, DName, ViewName]}=Req, Db) ->
+ QueryArgs = couch_httpd_view:parse_view_params(Req, nil, nil),
+ #view_query_args{
+ list = ListName
+ } = QueryArgs,
+ ?LOG_DEBUG("ici ~p", [ListName]),
+ case ListName of
+ nil -> couch_httpd_view:design_doc_view(Req, Db, DName, ViewName, nil);
+ _ ->
+ couch_httpd_show:handle_view_list(Req, DName, ListName, ViewName, Db, nil)
+ end;
+
+handle_db_view_req(#httpd{method='POST',
+ path_parts=[_Db, _View, DName, ViewName]}=Req, Db) ->
+ QueryArgs = couch_httpd_view:parse_view_params(Req, nil, nil),
+ #view_query_args{
+ list = ListName
+ } = QueryArgs,
+ case ListName of
+ nil ->
+ {Fields} = couch_httpd:json_body_obj(Req),
+ case proplists:get_value(<<"keys">>, Fields, nil) of
+ nil ->
+ Fmt = "POST to view ~p/~p in database ~p with no keys member.",
+ ?LOG_DEBUG(Fmt, [DName, ViewName, Db]),
+ couch_httpd_view:design_doc_view(Req, Db, DName, ViewName, nil);
+ Keys when is_list(Keys) ->
+ couch_httpd_view:design_doc_view(Req, Db, DName, ViewName, Keys);
+ _ ->
+ throw({bad_request, "`keys` member must be a array."})
+ end;
+ _ ->
+ ReqBody = couch_httpd:body(Req),
+ {Props2} = ?JSON_DECODE(ReqBody),
+ Keys = proplists:get_value(<<"keys">>, Props2, nil),
+ couch_httpd_show:handle_view_list(Req#httpd{req_body=ReqBody},
+ DName, ListName, ViewName, Db, Keys)
+ end;
+
+handle_db_view_req(Req, _Db) ->
+ send_method_not_allowed(Req, "GET,POST,HEAD").
+
handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
couch_stats_collector:increment({httpd, temporary_view_reads}),
{Props} = couch_httpd:json_body_obj(Req),
@@ -269,6 +312,8 @@ parse_view_param("reduce", Value) ->
[{reduce, parse_bool_param(Value)}];
parse_view_param("include_docs", Value) ->
[{include_docs, parse_bool_param(Value)}];
+parse_view_param("list", Value) ->
+ [{list, ?l2b(Value)}];
parse_view_param("callback", _) ->
[]; % Verified in the JSON response functions
parse_view_param(Key, Value) ->
@@ -298,6 +343,8 @@ validate_view_query(end_docid, Value, Args) ->
Args#view_query_args{end_docid=Value};
validate_view_query(limit, Value, Args) ->
Args#view_query_args{limit=Value};
+validate_view_query(list, Value, Args) ->
+ Args#view_query_args{list=Value};
validate_view_query(stale, _, Args) ->
Args;
validate_view_query(descending, true, Args) ->