Mutual exclusive: key, keys, start_key and end_key
- Key(s) should incompatible with start_key and end_key
- Treat single element keys as key
diff --git a/src/couch_mrview/src/couch_mrview_http.erl b/src/couch_mrview/src/couch_mrview_http.erl
index 9d35e87..8da8bba 100644
--- a/src/couch_mrview/src/couch_mrview_http.erl
+++ b/src/couch_mrview/src/couch_mrview_http.erl
@@ -486,7 +486,12 @@
Args0;
_ ->
% group_level set to undefined to detect if explicitly set by user
- Args0#mrargs{keys = Keys, group = undefined, group_level = undefined}
+ case Keys of
+ [_] ->
+ Args0#mrargs{group = undefined, group_level = undefined};
+ _ ->
+ Args0#mrargs{keys = Keys, group = undefined, group_level = undefined}
+ end
end,
lists:foldl(
fun({K, V}, Acc) ->
@@ -525,34 +530,147 @@
"reduce" ->
Args#mrargs{reduce = parse_boolean(Val)};
"key" when IsDecoded ->
- Args#mrargs{start_key = Val, end_key = Val};
+ case Args#mrargs.start_key =:= undefined andalso Args#mrargs.end_key =:= undefined of
+ true ->
+ Args#mrargs{start_key = Val, end_key = Val};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"key" ->
- JsonKey = ?JSON_DECODE(Val),
- Args#mrargs{start_key = JsonKey, end_key = JsonKey};
+ case Args#mrargs.start_key =:= undefined andalso Args#mrargs.end_key =:= undefined of
+ true ->
+ JsonKey = ?JSON_DECODE(Val),
+ Args#mrargs{start_key = JsonKey, end_key = JsonKey};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"keys" when IsDecoded ->
- Args#mrargs{keys = Val};
+ case Val of
+ [Val1] ->
+ case
+ Args#mrargs.start_key =:= undefined andalso
+ Args#mrargs.end_key =:= undefined
+ of
+ true ->
+ Args#mrargs{start_key = Val1, end_key = Val1};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
+ _ ->
+ Args#mrargs{keys = Val}
+ end;
"keys" ->
- Args#mrargs{keys = ?JSON_DECODE(Val)};
+ Val1 = ?JSON_DECODE(Val),
+ case Val1 of
+ [Val2] ->
+ case
+ Args#mrargs.start_key =:= undefined andalso
+ Args#mrargs.end_key =:= undefined
+ of
+ true ->
+ Args#mrargs{start_key = Val2, end_key = Val2};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
+ _ ->
+ Args#mrargs{keys = Val1}
+ end;
"startkey" when IsDecoded ->
- Args#mrargs{start_key = Val};
+ case Args#mrargs.start_key =:= undefined of
+ true ->
+ Args#mrargs{start_key = Val};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"start_key" when IsDecoded ->
- Args#mrargs{start_key = Val};
+ case Args#mrargs.start_key =:= undefined of
+ true ->
+ Args#mrargs{start_key = Val};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"startkey" ->
- Args#mrargs{start_key = ?JSON_DECODE(Val)};
+ case Args#mrargs.start_key =:= undefined of
+ true ->
+ Args#mrargs{start_key = ?JSON_DECODE(Val)};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"start_key" ->
- Args#mrargs{start_key = ?JSON_DECODE(Val)};
+ case Args#mrargs.start_key =:= undefined of
+ true ->
+ Args#mrargs{start_key = ?JSON_DECODE(Val)};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"startkey_docid" ->
Args#mrargs{start_key_docid = couch_util:to_binary(Val)};
"start_key_doc_id" ->
Args#mrargs{start_key_docid = couch_util:to_binary(Val)};
"endkey" when IsDecoded ->
- Args#mrargs{end_key = Val};
+ case Args#mrargs.end_key =:= undefined of
+ true ->
+ Args#mrargs{end_key = Val};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"end_key" when IsDecoded ->
- Args#mrargs{end_key = Val};
+ case Args#mrargs.end_key =:= undefined of
+ true ->
+ Args#mrargs{end_key = Val};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"endkey" ->
- Args#mrargs{end_key = ?JSON_DECODE(Val)};
+ case Args#mrargs.end_key =:= undefined of
+ true ->
+ Args#mrargs{end_key = ?JSON_DECODE(Val)};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"end_key" ->
- Args#mrargs{end_key = ?JSON_DECODE(Val)};
+ case Args#mrargs.end_key =:= undefined of
+ true ->
+ Args#mrargs{end_key = ?JSON_DECODE(Val)};
+ _ ->
+ throw(
+ {query_parse_error,
+ <<"`key(s)` is incompatible with `start_key` and `end_key`">>}
+ )
+ end;
"endkey_docid" ->
Args#mrargs{end_key_docid = couch_util:to_binary(Val)};
"end_key_doc_id" ->
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index e1e75f3..208e31d 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -564,7 +564,7 @@
{red, exact, _} ->
ok;
{red, _, KeyList} when is_list(KeyList) ->
- Msg = <<"Multi-key fetchs for reduce views must use `group=true`">>,
+ Msg = <<"Multi-key fetches for reduce views must use `group=true`">>,
mrverror(Msg);
_ ->
ok
@@ -581,13 +581,12 @@
ok;
{[], _, _} ->
ok;
+ {[Key], StartKey, EndKey} when Key =:= StartKey andalso Key =:= EndKey ->
+ ok;
{[_ | _], undefined, undefined} ->
ok;
_ ->
- mrverror(<<
- "`keys` is incompatible with `key`"
- ", `start_key` and `end_key`"
- >>)
+ mrverror(<<"`key(s)` is incompatible with `start_key` and `end_key`">>)
end,
case Args#mrargs.start_key_docid of
diff --git a/src/couch_mrview/test/eunit/couch_mrview_all_docs_tests.erl b/src/couch_mrview/test/eunit/couch_mrview_all_docs_tests.erl
index 1a81d4f..0079023 100644
--- a/src/couch_mrview/test/eunit/couch_mrview_all_docs_tests.erl
+++ b/src/couch_mrview/test/eunit/couch_mrview_all_docs_tests.erl
@@ -40,6 +40,7 @@
[
fun should_query/1,
fun should_query_with_range/1,
+ fun raise_error_query_with_range_and_keys/1,
fun should_query_with_range_rev/1,
fun should_query_with_limit_and_skip/1,
fun should_query_with_include_docs/1,
@@ -79,6 +80,12 @@
]},
?_assertEqual(Expect, Result).
+raise_error_query_with_range_and_keys(Db) ->
+ ?_assertThrow(
+ {query_parse_error, <<"`key(s)` is incompatible with `start_key` and `end_key`">>},
+ run_query(Db, [{keys, [<<"1">>]}, {start_key, <<"5">>}])
+ ).
+
should_query_with_range_rev(Db) ->
Result = run_query(Db, [
{direction, rev},
diff --git a/src/couch_mrview/test/eunit/couch_mrview_http_tests.erl b/src/couch_mrview/test/eunit/couch_mrview_http_tests.erl
index bfa4965..e903ab6 100644
--- a/src/couch_mrview/test/eunit/couch_mrview_http_tests.erl
+++ b/src/couch_mrview/test/eunit/couch_mrview_http_tests.erl
@@ -33,5 +33,61 @@
undefined,
#mrargs{}
)
+ ),
+
+ ?_assertEqual(
+ #mrargs{start_key = 1, end_key = 1, group_level = undefined, group = undefined},
+ couch_mrview_http:parse_params(
+ [{"key", "1"}],
+ undefined,
+ #mrargs{}
+ )
+ ),
+
+ ?_assertThrow(
+ {query_parse_error, <<"`key(s)` is incompatible with `start_key` and `end_key`">>},
+ couch_mrview_http:parse_params(
+ [{"key", "1"}, {"start_key", "2"}],
+ undefined,
+ #mrargs{}
+ )
+ ),
+
+ ?_assertThrow(
+ {query_parse_error, <<"`key(s)` is incompatible with `start_key` and `end_key`">>},
+ couch_mrview_http:parse_params(
+ [{"end_key", "5"}, {"key", "1"}],
+ undefined,
+ #mrargs{}
+ )
+ ),
+
+ ?_assertThrow(
+ {query_parse_error, <<"`key(s)` is incompatible with `start_key` and `end_key`">>},
+ couch_mrview_http:parse_params(
+ [{"keys", "[1]"}, {"end_key", "5"}],
+ undefined,
+ #mrargs{}
+ )
+ ),
+
+ ?_assertEqual(
+ #mrargs{start_key = 1, end_key = 1, group_level = undefined},
+ couch_mrview_http:parse_params(
+ [{"keys", "[1]"}],
+ undefined,
+ #mrargs{}
+ )
+ ),
+
+ ?_assertEqual(
+ #mrargs{
+ keys = [1, 2], start_key = undefined, end_key = undefined, group_level = undefined
+ },
+ couch_mrview_http:parse_params(
+ [{"keys", "[1, 2]"}],
+ undefined,
+ #mrargs{}
+ )
)
].
diff --git a/src/couch_mrview/test/eunit/couch_mrview_red_views_tests.erl b/src/couch_mrview/test/eunit/couch_mrview_red_views_tests.erl
index b6042b6..851ce3b 100644
--- a/src/couch_mrview/test/eunit/couch_mrview_red_views_tests.erl
+++ b/src/couch_mrview/test/eunit/couch_mrview_red_views_tests.erl
@@ -40,6 +40,7 @@
[
fun should_reduce_basic/1,
fun should_reduce_key_range/1,
+ fun raise_error_keys_without_group/1,
fun should_reduce_with_group_level/1,
fun should_reduce_with_group_exact/1
]
@@ -65,6 +66,12 @@
]},
?_assertEqual(Expect, Result).
+raise_error_keys_without_group(Db) ->
+ ?_assertThrow(
+ {query_parse_error, <<"Multi-key fetches for reduce views must use `group=true`">>},
+ run_query(Db, [{keys, [[0, 2], [0, 4]]}])
+ ).
+
should_reduce_with_group_level(Db) ->
Result = run_query(Db, [{group_level, 1}]),
Expect =