diff options
author | jiahuili <Jiahui.Li@ibm.com> | 2023-02-03 10:53:04 -0600 |
---|---|---|
committer | jiahuili <lijiahui702@gmail.com> | 2023-03-06 11:08:27 -0600 |
commit | 465468f2081a2da4ceb2b07a89a4b8b324dedffa (patch) | |
tree | 032f88c1b641faff51cf54a30da1608ce7d6e4c5 | |
parent | 0db9154757a102336dc2d267370bcd23955762de (diff) | |
download | couchdb-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
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 = |