summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--NEWS2
-rw-r--r--THANKS2
-rw-r--r--share/www/script/test/delayed_commits.js29
-rw-r--r--share/www/script/test/reduce_builtin.js20
-rw-r--r--share/www/script/test/view_update_seq.js7
-rw-r--r--src/couchdb/couch_db.erl6
-rw-r--r--src/couchdb/couch_db_updater.erl2
-rw-r--r--src/couchdb/couch_httpd_db.erl15
-rw-r--r--src/couchdb/couch_httpd_view.erl2
-rw-r--r--src/couchdb/couch_query_servers.erl8
-rwxr-xr-xtest/etap/160-vhosts.t2
12 files changed, 87 insertions, 14 deletions
diff --git a/CHANGES b/CHANGES
index 65768af4..384b3f38 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,11 @@ Apache CouchDB CHANGES
Version 1.0.1
-------------
+Storage System:
+
+ * Fix data corruption bug COUCHDB-844. Please see
+ http://couchdb.apache.org/notice/1.0.1.html for details.
+
Replicator:
* Added support for replication via an HTTP/HTTP proxy.
@@ -12,6 +17,7 @@ Replicator:
HTTP Interface:
+ * Expose `committed_update_seq` for monitoring purposes.
* Show fields saved along with _deleted=true. Allows for auditing of deletes.
* More robust Accept-header detection.
diff --git a/NEWS b/NEWS
index 28e60787..5f7e3306 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ Each release section notes when backwards incompatible changes have been made.
Version 1.0.1
-------------
+ * Fix data corruption bug COUCHDB-844. Please see
+ http://couchdb.apache.org/notice/1.0.1.html for details.
* Added support for replication via an HTTP/HTTP proxy.
* Fixed various replicator bugs for interop with older CouchDB versions.
* Show fields saved along with _deleted=true. Allows for auditing of deletes.
diff --git a/THANKS b/THANKS
index 456a3960..ebee4845 100644
--- a/THANKS
+++ b/THANKS
@@ -63,5 +63,7 @@ suggesting improvements or submitting changes. Some of these people are:
* Paul Bonser <pib@paulbonser.com>
* Caleb Land <caleb.land@gmail.com>
* Juhani Ränkimies <juhani@juranki.com>
+ * Lim Yue Chuan <shasderias@gmail.com>
+ * David Davis <xantus@xantus.org>
For a list of authors see the `AUTHORS` file.
diff --git a/share/www/script/test/delayed_commits.js b/share/www/script/test/delayed_commits.js
index d0c87182..dbb072fb 100644
--- a/share/www/script/test/delayed_commits.js
+++ b/share/www/script/test/delayed_commits.js
@@ -122,4 +122,33 @@ couchTests.delayed_commits = function(debug) {
}
});
+
+ // Test that a conflict can't cause delayed commits to fail
+ run_on_modified_server(
+ [{section: "couchdb",
+ key: "delayed_commits",
+ value: "true"}],
+
+ function() {
+ //First save a document and commit it
+ T(db.save({_id:"6",a:2,b:4}).ok);
+ T(db.ensureFullCommit().ok);
+ //Generate a conflict
+ try {
+ db.save({_id:"6",a:2,b:4});
+ } catch( e) {
+ T(e.error == "conflict");
+ }
+ //Wait for the delayed commit interval to pass
+ var time = new Date();
+ while(new Date() - time < 2000);
+ //Save a new doc
+ T(db.save({_id:"7",a:2,b:4}).ok);
+ //Wait for the delayed commit interval to pass
+ var time = new Date();
+ while(new Date() - time < 2000);
+ //Crash the server and make sure the last doc was written
+ restartServer();
+ T(db.open("7") != null);
+ });
};
diff --git a/share/www/script/test/reduce_builtin.js b/share/www/script/test/reduce_builtin.js
index 0db6bc09..d9635688 100644
--- a/share/www/script/test/reduce_builtin.js
+++ b/share/www/script/test/reduce_builtin.js
@@ -72,6 +72,26 @@ couchTests.reduce_builtin = function(debug) {
T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
}
+ // test for trailing characters after builtin functions, desired behaviour
+ // is to disregard any trailing characters
+ // I think the behavior should be a prefix test, so that even "_statsorama"
+ // or "_stats\nare\awesome" should work just as "_stats" does. - JChris
+
+ var trailing = ["\u000a", "orama", "\nare\nawesome", " ", " \n "];
+
+ for(var i=0; i < trailing.length; i++) {
+ result = db.query(map, "_sum" + trailing[i]);
+ T(result.rows[0].value == 2*summate(numDocs));
+ result = db.query(map, "_count" + trailing[i]);
+ T(result.rows[0].value == 1000);
+ result = db.query(map, "_stats" + trailing[i]);
+ T(result.rows[0].value.sum == 2*summate(numDocs));
+ T(result.rows[0].value.count == 1000);
+ T(result.rows[0].value.min == 1);
+ T(result.rows[0].value.max == 500);
+ T(result.rows[0].value.sumsqr == 2*sumsqr(numDocs));
+ }
+
db.deleteDb();
db.createDb();
diff --git a/share/www/script/test/view_update_seq.js b/share/www/script/test/view_update_seq.js
index cfda1a87..e6be3f70 100644
--- a/share/www/script/test/view_update_seq.js
+++ b/share/www/script/test/view_update_seq.js
@@ -18,6 +18,11 @@ couchTests.view_update_seq = function(debug) {
T(db.info().update_seq == 0);
+ resp = db.allDocs({update_seq:true});
+
+ T(resp.rows.length == 0);
+ T(resp.update_seq == 0);
+
var designDoc = {
_id:"_design/test",
language: "javascript",
@@ -35,7 +40,7 @@ couchTests.view_update_seq = function(debug) {
T(db.info().update_seq == 1);
- var resp = db.allDocs({update_seq:true});
+ resp = db.allDocs({update_seq:true});
T(resp.rows.length == 1);
T(resp.update_seq == 1);
diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl
index 34b4f3fb..7678f6ca 100644
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@ -239,7 +239,8 @@ get_db_info(Db) ->
update_seq=SeqNum,
name=Name,
fulldocinfo_by_id_btree=FullDocBtree,
- instance_start_time=StartTime} = Db,
+ instance_start_time=StartTime,
+ committed_update_seq=CommittedUpdateSeq} = Db,
{ok, Size} = couch_file:bytes(Fd),
{ok, {Count, DelCount}} = couch_btree:full_reduce(FullDocBtree),
InfoList = [
@@ -251,7 +252,8 @@ get_db_info(Db) ->
{compact_running, Compactor/=nil},
{disk_size, Size},
{instance_start_time, StartTime},
- {disk_format_version, DiskVersion}
+ {disk_format_version, DiskVersion},
+ {committed_update_seq, CommittedUpdateSeq}
],
{ok, InfoList}.
diff --git a/src/couchdb/couch_db_updater.erl b/src/couchdb/couch_db_updater.erl
index f0879700..19a4c165 100644
--- a/src/couchdb/couch_db_updater.erl
+++ b/src/couchdb/couch_db_updater.erl
@@ -701,7 +701,7 @@ commit_data(Db, _) ->
if is_reference(Timer) -> erlang:cancel_timer(Timer); true -> ok end,
case db_to_header(Db, OldHeader) of
OldHeader ->
- Db;
+ Db#db{waiting_delayed_commit=nil};
Header ->
case lists:member(before_header, FsyncOptions) of
true -> ok = couch_file:sync(Filepath);
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index b8d3157e..cf4e2120 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -486,7 +486,13 @@ all_docs_view(Req, Db, Keys) ->
true -> EndDocId
end,
FoldAccInit = {Limit, SkipCount, undefined, []},
- UpdateSeq = couch_db:get_update_seq(Db),
+ UpdateSeq = couch_db:get_update_seq(Db),
+ JsonParams = case couch_httpd:qs_value(Req, "update_seq") of
+ "true" ->
+ [{update_seq, UpdateSeq}];
+ _Else ->
+ []
+ end,
case Keys of
nil ->
FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, UpdateSeq,
@@ -504,7 +510,7 @@ all_docs_view(Req, Db, Keys) ->
{ok, LastOffset, FoldResult} = couch_db:enum_docs(Db,
AdapterFun, FoldAccInit, [{start_key, StartId}, {dir, Dir},
{if Inclusive -> end_key; true -> end_key_gt end, EndId}]),
- couch_httpd_view:finish_view_fold(Req, TotalRowCount, LastOffset, FoldResult);
+ couch_httpd_view:finish_view_fold(Req, TotalRowCount, LastOffset, FoldResult, JsonParams);
_ ->
FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, UpdateSeq,
TotalRowCount, #view_fold_helper_funs{
@@ -533,7 +539,7 @@ all_docs_view(Req, Db, Keys) ->
{_, FoldAcc2} = FoldlFun(Doc, 0, FoldAcc),
FoldAcc2
end, FoldAccInit, Keys),
- couch_httpd_view:finish_view_fold(Req, TotalRowCount, 0, FoldResult)
+ couch_httpd_view:finish_view_fold(Req, TotalRowCount, 0, FoldResult, JsonParams)
end
end).
@@ -1025,7 +1031,8 @@ db_attachment_req(#httpd{method=Method,mochi_req=MochiReq}=Req, Db, DocId, FileN
'DELETE' ->
{200, []};
_ ->
- {201, [{"Location", absolute_uri(Req, "/" ++
+ {201, [{"Etag", "\"" ++ ?b2l(couch_doc:rev_to_str(UpdatedRev)) ++ "\""},
+ {"Location", absolute_uri(Req, "/" ++
binary_to_list(DbName) ++ "/" ++
binary_to_list(DocId) ++ "/" ++
binary_to_list(FileName)
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index fc0c24a8..e1a0dfad 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -16,7 +16,7 @@
-export([handle_view_req/3,handle_temp_view_req/2]).
-export([get_stale_type/1, get_reduce_type/1, parse_view_params/3]).
--export([make_view_fold_fun/7, finish_view_fold/4, view_row_obj/3]).
+-export([make_view_fold_fun/7, finish_view_fold/4, finish_view_fold/5, view_row_obj/3]).
-export([view_group_etag/2, view_group_etag/3, make_reduce_fold_funs/6]).
-export([design_doc_view/5, parse_bool_param/1, doc_member/2]).
-export([make_key_options/1, load_view/4]).
diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl
index b2aa7a12..c4f1bf0b 100644
--- a/src/couchdb/couch_query_servers.erl
+++ b/src/couchdb/couch_query_servers.erl
@@ -143,16 +143,16 @@ os_rereduce(Lang, OsRedSrcs, KVs) ->
builtin_reduce(_Re, [], _KVs, Acc) ->
{ok, lists:reverse(Acc)};
-builtin_reduce(Re, [<<"_sum">>|BuiltinReds], KVs, Acc) ->
+builtin_reduce(Re, [<<"_sum",_/binary>>|BuiltinReds], KVs, Acc) ->
Sum = builtin_sum_rows(KVs),
builtin_reduce(Re, BuiltinReds, KVs, [Sum|Acc]);
-builtin_reduce(reduce, [<<"_count">>|BuiltinReds], KVs, Acc) ->
+builtin_reduce(reduce, [<<"_count",_/binary>>|BuiltinReds], KVs, Acc) ->
Count = length(KVs),
builtin_reduce(reduce, BuiltinReds, KVs, [Count|Acc]);
-builtin_reduce(rereduce, [<<"_count">>|BuiltinReds], KVs, Acc) ->
+builtin_reduce(rereduce, [<<"_count",_/binary>>|BuiltinReds], KVs, Acc) ->
Count = builtin_sum_rows(KVs),
builtin_reduce(rereduce, BuiltinReds, KVs, [Count|Acc]);
-builtin_reduce(Re, [<<"_stats">>|BuiltinReds], KVs, Acc) ->
+builtin_reduce(Re, [<<"_stats",_/binary>>|BuiltinReds], KVs, Acc) ->
Stats = builtin_stats(Re, KVs),
builtin_reduce(Re, BuiltinReds, KVs, [Stats|Acc]).
diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.t
index 77d9d58b..eb704d31 100755
--- a/test/etap/160-vhosts.t
+++ b/test/etap/160-vhosts.t
@@ -105,7 +105,7 @@ test_regular_request() ->
test_vhost_request() ->
case ibrowse:send_req(server(), [], get, [], [{host_header, "example.com"}]) of
{ok, _, _, Body} ->
- {[{<<"db_name">>, <<"etap-test-db">>},_,_,_,_,_,_,_,_]}
+ {[{<<"db_name">>, <<"etap-test-db">>},_,_,_,_,_,_,_,_,_]}
= couch_util:json_decode(Body),
etap:is(true, true, "should return database info");
_Else -> false