summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjiahuili <Jiahui.Li@ibm.com>2023-02-03 10:53:04 -0600
committerjiahuili <lijiahui702@gmail.com>2023-03-06 11:08:27 -0600
commit465468f2081a2da4ceb2b07a89a4b8b324dedffa (patch)
tree032f88c1b641faff51cf54a30da1608ce7d6e4c5
parent0db9154757a102336dc2d267370bcd23955762de (diff)
downloadcouchdb-465468f2081a2da4ceb2b07a89a4b8b324dedffa.tar.gz
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
-rw-r--r--src/couch_mrview/src/couch_mrview_http.erl146
-rw-r--r--src/couch_mrview/src/couch_mrview_util.erl9
-rw-r--r--src/couch_mrview/test/eunit/couch_mrview_all_docs_tests.erl7
-rw-r--r--src/couch_mrview/test/eunit/couch_mrview_http_tests.erl56
-rw-r--r--src/couch_mrview/test/eunit/couch_mrview_red_views_tests.erl7
5 files changed, 206 insertions, 19 deletions
diff --git a/src/couch_mrview/src/couch_mrview_http.erl b/src/couch_mrview/src/couch_mrview_http.erl
index 9d35e873a..8da8bba8e 100644
--- a/src/couch_mrview/src/couch_mrview_http.erl
+++ b/src/couch_mrview/src/couch_mrview_http.erl
@@ -486,7 +486,12 @@ parse_params(Props, Keys, #mrargs{} = Args0, Options) ->
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 @@ parse_param(Key, Val, Args, IsDecoded) ->
"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 e1e75f34f..208e31d3c 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -564,7 +564,7 @@ validate_args(Args) ->
{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 @@ validate_args(Args) ->
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 1a81d4f0a..00790235b 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 @@ all_docs_test_() ->
[
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 @@ should_query_with_range(Db) ->
]},
?_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 bfa4965a4..e903ab6be 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 @@ mrview_http_test_() ->
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 b6042b6c7..851ce3bc2 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 @@ reduce_views_test_() ->
[
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 @@ should_reduce_key_range(Db) ->
]},
?_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 =