summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Joseph Davis <davisp@apache.org>2011-08-15 03:57:48 +0000
committerPaul Joseph Davis <davisp@apache.org>2011-08-15 03:57:48 +0000
commit0dc0f8b41f627876f65183ea1d99c68b16abeae0 (patch)
treecc8b672ac2c4f3b7a38c05c3c0de0be023369b15
parent2eb62337efc1171d1ea1e4392f8cacf0dabc1ab0 (diff)
Fix empty range check for raw collation.
The check for empty ranges was not taking into account the view option for raw collation. This fixes that by passing the couch_btree:less/2 function into the check. Patch by: Jason Smith Back port of: 1156506, 1156507, 1156509, 1156509, 1156610 git-svn-id: https://svn.apache.org/repos/asf/couchdb/branches/1.1.x@1157706 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--share/www/script/test/all_docs.js7
-rw-r--r--share/www/script/test/view_collation_raw.js9
-rw-r--r--src/couchdb/couch_btree.erl1
-rw-r--r--src/couchdb/couch_httpd_db.erl4
-rw-r--r--src/couchdb/couch_httpd_view.erl54
5 files changed, 56 insertions, 19 deletions
diff --git a/share/www/script/test/all_docs.js b/share/www/script/test/all_docs.js
index 1d83aa95..1afe701d 100644
--- a/share/www/script/test/all_docs.js
+++ b/share/www/script/test/all_docs.js
@@ -41,6 +41,13 @@ couchTests.all_docs = function(debug) {
var all = db.allDocs({startkey:"2"});
T(all.offset == 2);
+ // Confirm that queries may assume raw collation.
+ var raw = db.allDocs({
+ startkey: "org.couchdb.user:",
+ endkey: "org.couchdb.user;"
+ });
+ TEquals(0, raw.rows.length);
+
// check that the docs show up in the seq view in the order they were created
var changes = db.changes();
var ids = ["0","3","1","2"];
diff --git a/share/www/script/test/view_collation_raw.js b/share/www/script/test/view_collation_raw.js
index 31624cdb..779f7eb8 100644
--- a/share/www/script/test/view_collation_raw.js
+++ b/share/www/script/test/view_collation_raw.js
@@ -76,12 +76,19 @@ couchTests.view_collation_raw = function(debug) {
}
}
T(db.save(designDoc).ok);
+
+ // Confirm that everything collates correctly.
var rows = db.view("test/test").rows;
for (i=0; i<values.length; i++) {
T(equals(rows[i].key, values[i]));
}
- // everything has collated correctly. Now to check the descending output
+ // Confirm that couch allows raw semantics in key ranges.
+ rows = db.view("test/test", {startkey:"Z", endkey:"a"}).rows;
+ TEquals(1, rows.length);
+ TEquals("a", rows[0].key);
+
+ // Check the descending output.
rows = db.view("test/test", {descending: true}).rows;
for (i=0; i<values.length; i++) {
T(equals(rows[i].key, values[values.length - 1 -i]));
diff --git a/src/couchdb/couch_btree.erl b/src/couchdb/couch_btree.erl
index 91bc8f1b..0ddf7a48 100644
--- a/src/couchdb/couch_btree.erl
+++ b/src/couchdb/couch_btree.erl
@@ -15,6 +15,7 @@
-export([open/2, open/3, query_modify/4, add/2, add_remove/3]).
-export([fold/4, full_reduce/1, final_reduce/2, foldl/3, foldl/4]).
-export([fold_reduce/4, lookup/2, get_state/1, set_options/2]).
+-export([less/3]).
-define(CHUNK_THRESHOLD, 16#4ff).
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 71204598..db430bbd 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -477,6 +477,7 @@ db_req(#httpd{path_parts=[_, DocId | FileNameParts]}=Req, Db) ->
db_attachment_req(Req, Db, DocId, FileNameParts).
all_docs_view(Req, Db, Keys) ->
+ RawCollator = fun(A, B) -> A < B end,
#view_query_args{
start_key = StartKey,
start_docid = StartDocId,
@@ -486,7 +487,8 @@ all_docs_view(Req, Db, Keys) ->
skip = SkipCount,
direction = Dir,
inclusive_end = Inclusive
- } = QueryArgs = couch_httpd_view:parse_view_params(Req, Keys, map),
+ } = QueryArgs
+ = couch_httpd_view:parse_view_params(Req, Keys, map, RawCollator),
{ok, Info} = couch_db:get_db_info(Db),
CurrentEtag = couch_httpd:make_etag(Info),
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 1f279417..082a5039 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -15,7 +15,7 @@
-export([handle_view_req/3,handle_temp_view_req/2]).
--export([parse_view_params/3]).
+-export([parse_view_params/4]).
-export([make_view_fold_fun/7, finish_view_fold/4, finish_view_fold/5, view_row_obj/4]).
-export([view_etag/5, make_reduce_fold_funs/6]).
-export([design_doc_view/5, parse_bool_param/1, doc_member/3]).
@@ -34,18 +34,19 @@ design_doc_view(Req, Db, DName, ViewName, Keys) ->
Reduce = get_reduce_type(Req),
Result = case couch_view:get_map_view(Db, DesignId, ViewName, Stale) of
{ok, View, Group} ->
- QueryArgs = parse_view_params(Req, Keys, map),
+ QueryArgs = parse_view_params(Req, Keys, map, view_collator(View)),
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
{not_found, Reason} ->
case couch_view:get_reduce_view(Db, DesignId, ViewName, Stale) of
{ok, ReduceView, Group} ->
+ Collator = view_collator(ReduceView),
case Reduce of
false ->
- QueryArgs = parse_view_params(Req, Keys, red_map),
+ QueryArgs = parse_view_params(Req, Keys, red_map, Collator),
MapView = couch_view:extract_map_view(ReduceView),
output_map_view(Req, MapView, Group, Db, QueryArgs, Keys);
_ ->
- QueryArgs = parse_view_params(Req, Keys, reduce),
+ QueryArgs = parse_view_params(Req, Keys, reduce, Collator),
output_reduce_view(Req, Db, ReduceView, Group, QueryArgs, Keys)
end;
_ ->
@@ -90,19 +91,19 @@ handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
Reduce = get_reduce_type(Req),
case couch_util:get_value(<<"reduce">>, Props, null) of
null ->
- QueryArgs = parse_view_params(Req, Keys, map),
{ok, View, Group} = couch_view:get_temp_map_view(Db, Language,
DesignOptions, MapSrc),
+ QueryArgs = parse_view_params(Req, Keys, map, view_collator(View)),
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
_ when Reduce =:= false ->
- QueryArgs = parse_view_params(Req, Keys, red_map),
{ok, View, Group} = couch_view:get_temp_map_view(Db, Language,
DesignOptions, MapSrc),
+ QueryArgs = parse_view_params(Req, Keys, red_map, view_collator(View)),
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
RedSrc ->
- QueryArgs = parse_view_params(Req, Keys, reduce),
{ok, View, Group} = couch_view:get_temp_reduce_view(Db, Language,
DesignOptions, MapSrc, RedSrc),
+ QueryArgs = parse_view_params(Req, Keys, reduce, view_collator(View)),
output_reduce_view(Req, Db, View, Group, QueryArgs, Keys)
end;
@@ -209,18 +210,19 @@ load_view(Req, Db, {ViewDesignId, ViewName}, Keys) ->
Reduce = get_reduce_type(Req),
case couch_view:get_map_view(Db, ViewDesignId, ViewName, Stale) of
{ok, View, Group} ->
- QueryArgs = parse_view_params(Req, Keys, map),
+ QueryArgs = parse_view_params(Req, Keys, map, view_collator(View)),
{map, View, Group, QueryArgs};
{not_found, _Reason} ->
case couch_view:get_reduce_view(Db, ViewDesignId, ViewName, Stale) of
{ok, ReduceView, Group} ->
+ Collator = view_collator(ReduceView),
case Reduce of
false ->
- QueryArgs = parse_view_params(Req, Keys, map_red),
+ QueryArgs = parse_view_params(Req, Keys, map_red, Collator),
MapView = couch_view:extract_map_view(ReduceView),
{map, MapView, Group, QueryArgs};
_ ->
- QueryArgs = parse_view_params(Req, Keys, reduce),
+ QueryArgs = parse_view_params(Req, Keys, reduce, Collator),
{reduce, ReduceView, Group, QueryArgs}
end;
{not_found, Reason} ->
@@ -228,12 +230,30 @@ load_view(Req, Db, {ViewDesignId, ViewName}, Keys) ->
end
end.
+view_collator({reduce, _N, _Lang, View}) ->
+ view_collator(View);
+
+view_collator({temp_reduce, View}) ->
+ view_collator(View);
+
+view_collator(#view{btree=Btree}) ->
+ % Return an "is-less-than" predicate by calling into the btree's
+ % collator. For raw collation, couch_btree compares arbitrary
+ % Erlang terms, but for normal (ICU) collation, it expects
+ % {Json, Id} tuples.
+ fun
+ ({_JsonA, _IdA}=A, {_JsonB, _IdB}=B) ->
+ couch_btree:less(Btree, A, B);
+ (JsonA, JsonB) ->
+ couch_btree:less(Btree, {JsonA, null}, {JsonB, null})
+ end.
+
% query_parse_error could be removed
% we wouldn't need to pass the view type, it'd just parse params.
% I'm not sure what to do about the error handling, but
% it might simplify things to have a parse_view_params function
% that doesn't throw().
-parse_view_params(Req, Keys, ViewType) ->
+parse_view_params(Req, Keys, ViewType, LessThan) ->
QueryList = couch_httpd:qs(Req),
QueryParams =
lists:foldl(fun({K, V}, Acc) ->
@@ -247,7 +267,7 @@ parse_view_params(Req, Keys, ViewType) ->
QueryArgs = lists:foldl(fun({K, V}, Args2) ->
validate_view_query(K, V, Args2)
end, Args, lists:reverse(QueryParams)), % Reverse to match QS order.
- warn_on_empty_key_range(QueryArgs),
+ warn_on_empty_key_range(QueryArgs, LessThan),
GroupLevel = QueryArgs#view_query_args.group_level,
case {ViewType, GroupLevel, IsMultiGet} of
{reduce, exact, true} ->
@@ -328,15 +348,15 @@ parse_view_param("callback", _) ->
parse_view_param(Key, Value) ->
[{extra, {Key, Value}}].
-warn_on_empty_key_range(#view_query_args{start_key=undefined}) ->
+warn_on_empty_key_range(#view_query_args{start_key=undefined}, _Lt) ->
ok;
-warn_on_empty_key_range(#view_query_args{end_key=undefined}) ->
+warn_on_empty_key_range(#view_query_args{end_key=undefined}, _Lt) ->
ok;
-warn_on_empty_key_range(#view_query_args{start_key=A, end_key=A}) ->
+warn_on_empty_key_range(#view_query_args{start_key=A, end_key=A}, _Lt) ->
ok;
warn_on_empty_key_range(#view_query_args{
- start_key=StartKey, end_key=EndKey, direction=Dir}) ->
- case {Dir, couch_view:less_json(StartKey, EndKey)} of
+ start_key=StartKey, end_key=EndKey, direction=Dir}, LessThan) ->
+ case {Dir, LessThan(StartKey, EndKey)} of
{fwd, false} ->
throw({query_parse_error,
<<"No rows can match your key range, reverse your ",