summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/test/replication.js94
-rw-r--r--src/couchdb/couch_rep.erl45
2 files changed, 97 insertions, 42 deletions
diff --git a/share/www/script/test/replication.js b/share/www/script/test/replication.js
index b92c3fcd..60051a67 100644
--- a/share/www/script/test/replication.js
+++ b/share/www/script/test/replication.js
@@ -452,31 +452,17 @@ couchTests.replication = function(debug) {
}
// test filtered replication
-
- var sourceDb = new CouchDB(
- "test_suite_filtered_rep_db_a", {"X-Couch-Full-Commit":"false"}
- );
-
- sourceDb.deleteDb();
- sourceDb.createDb();
-
- T(sourceDb.save({_id:"foo1",value:1}).ok);
- T(sourceDb.save({_id:"foo2",value:2}).ok);
- T(sourceDb.save({_id:"foo3",value:3}).ok);
- T(sourceDb.save({_id:"foo4",value:4}).ok);
- T(sourceDb.save({
- "_id": "_design/mydesign",
- "language" : "javascript",
- "filters" : {
- "myfilter" : (function(doc, req) {
- if (doc.value < Number(req.query.maxvalue)) {
- return true;
- } else {
- return false;
- }
- }).toString()
+ var filterFun1 = (function(doc, req) {
+ if (doc.value < Number(req.query.maxvalue)) {
+ return true;
+ } else {
+ return false;
}
- }).ok);
+ }).toString();
+
+ var filterFun2 = (function(doc, req) {
+ return true;
+ }).toString();
var dbPairs = [
{source:"test_suite_filtered_rep_db_a",
@@ -488,9 +474,28 @@ couchTests.replication = function(debug) {
{source:CouchDB.protocol + host + "/test_suite_filtered_rep_db_a",
target:CouchDB.protocol + host + "/test_suite_filtered_rep_db_b"}
];
+ var sourceDb = new CouchDB("test_suite_filtered_rep_db_a");
+ var targetDb = new CouchDB("test_suite_filtered_rep_db_b");
for (var i = 0; i < dbPairs.length; i++) {
- var targetDb = new CouchDB("test_suite_filtered_rep_db_b");
+ sourceDb.deleteDb();
+ sourceDb.createDb();
+
+ T(sourceDb.save({_id: "foo1", value: 1}).ok);
+ T(sourceDb.save({_id: "foo2", value: 2}).ok);
+ T(sourceDb.save({_id: "foo3", value: 3}).ok);
+ T(sourceDb.save({_id: "foo4", value: 4}).ok);
+
+ var ddoc = {
+ "_id": "_design/mydesign",
+ "language": "javascript",
+ "filters": {
+ "myfilter": filterFun1
+ }
+ };
+
+ T(sourceDb.save(ddoc).ok);
+
targetDb.deleteDb();
targetDb.createDb();
@@ -526,6 +531,45 @@ couchTests.replication = function(debug) {
var docFoo4 = targetDb.open("foo4");
T(docFoo4 === null);
+
+ // replication should start from scratch after the filter's code changed
+
+ ddoc.filters.myfilter = filterFun2;
+ T(sourceDb.save(ddoc).ok);
+
+ repResult = CouchDB.replicate(dbA, dbB, {
+ body: {
+ "filter" : "mydesign/myfilter",
+ "query_params" : {
+ "maxvalue": "3"
+ }
+ }
+ });
+
+ T(repResult.ok);
+ T(repResult.history instanceof Array);
+ T(repResult.history.length === 1);
+ T(repResult.history[0].docs_written === 3);
+ T(repResult.history[0].docs_read === 3);
+ T(repResult.history[0].doc_write_failures === 0);
+
+ docFoo1 = targetDb.open("foo1");
+ T(docFoo1 !== null);
+ T(docFoo1.value === 1);
+
+ docFoo2 = targetDb.open("foo2");
+ T(docFoo2 !== null);
+ T(docFoo2.value === 2);
+
+ docFoo3 = targetDb.open("foo3");
+ T(docFoo3 !== null);
+ T(docFoo3.value === 3);
+
+ docFoo4 = targetDb.open("foo4");
+ T(docFoo4 !== null);
+ T(docFoo4.value === 4);
+
+ T(targetDb.open("_design/mydesign") !== null);
}
};
diff --git a/src/couchdb/couch_rep.erl b/src/couchdb/couch_rep.erl
index 6dbd3c7c..85ecc99e 100644
--- a/src/couchdb/couch_rep.erl
+++ b/src/couchdb/couch_rep.erl
@@ -486,14 +486,14 @@ make_replication_id({Props}, UserCtx, 2) ->
Port = mochiweb_socket_server:get(couch_httpd, port),
Src = get_rep_endpoint(UserCtx, ?getv(<<"source">>, Props)),
Tgt = get_rep_endpoint(UserCtx, ?getv(<<"target">>, Props)),
- maybe_append_filters({Props}, [HostName, Port, Src, Tgt]);
+ maybe_append_filters({Props}, [HostName, Port, Src, Tgt], UserCtx);
make_replication_id({Props}, UserCtx, 1) ->
{ok, HostName} = inet:gethostname(),
Src = get_rep_endpoint(UserCtx, ?getv(<<"source">>, Props)),
Tgt = get_rep_endpoint(UserCtx, ?getv(<<"target">>, Props)),
- maybe_append_filters({Props}, [HostName, Src, Tgt]).
+ maybe_append_filters({Props}, [HostName, Src, Tgt], UserCtx).
-maybe_append_filters({Props}, Base) ->
+maybe_append_filters({Props}, Base, UserCtx) ->
Base2 = Base ++
case ?getv(<<"filter">>, Props) of
undefined ->
@@ -504,10 +504,20 @@ maybe_append_filters({Props}, Base) ->
[DocIds]
end;
Filter ->
- [Filter, ?getv(<<"query_params">>, Props, {[]})]
+ [filter_code(Filter, Props, UserCtx),
+ ?getv(<<"query_params">>, Props, {[]})]
end,
couch_util:to_hex(couch_util:md5(term_to_binary(Base2))).
+filter_code(Filter, Props, UserCtx) ->
+ {match, [DDocName, FilterName]} =
+ re:run(Filter, "(.*?)/(.*)", [{capture, [1, 2], binary}]),
+ ProxyParams = parse_proxy_params(?getv(<<"proxy">>, Props, [])),
+ Source = open_db(?getv(<<"source">>, Props), UserCtx, ProxyParams),
+ {ok, #doc{body = Body}} = open_doc(Source, <<"_design/", DDocName/binary>>),
+ Code = couch_util:get_nested_json_value(Body, [<<"filters">>, FilterName]),
+ re:replace(Code, "^\s*(.*?)\s*$", "\\1", [{return, binary}]).
+
maybe_add_trailing_slash(Url) ->
re:replace(Url, "[^/]$", "&/", [{return, list}]).
@@ -556,26 +566,27 @@ fold_replication_logs([Db|Rest]=Dbs, Vsn, LogId, NewId,
RepProps, UserCtx, [MigratedLog|Acc])
end.
-open_replication_log(#http_db{}=Db, DocId) ->
- Req = Db#http_db{resource=couch_util:encode_doc_id(DocId)},
- case couch_rep_httpc:request(Req) of
- {[{<<"error">>, _}, {<<"reason">>, _}]} ->
- ?LOG_DEBUG("didn't find a replication log for ~s", [Db#http_db.url]),
- {error, not_found};
- Doc ->
- ?LOG_DEBUG("found a replication log for ~s", [Db#http_db.url]),
- {ok, couch_doc:from_json_obj(Doc)}
- end;
open_replication_log(Db, DocId) ->
- case couch_db:open_doc(Db, DocId, []) of
+ case open_doc(Db, DocId) of
{ok, Doc} ->
- ?LOG_DEBUG("found a replication log for ~s", [Db#db.name]),
+ ?LOG_DEBUG("found a replication log for ~s", [dbname(Db)]),
{ok, Doc};
_ ->
- ?LOG_DEBUG("didn't find a replication log for ~s", [Db#db.name]),
+ ?LOG_DEBUG("didn't find a replication log for ~s", [dbname(Db)]),
{error, not_found}
end.
+open_doc(#http_db{} = Db, DocId) ->
+ Req = Db#http_db{resource = couch_util:encode_doc_id(DocId)},
+ case couch_rep_httpc:request(Req) of
+ {[{<<"error">>, _}, {<<"reason">>, _}]} ->
+ {error, not_found};
+ Doc ->
+ {ok, couch_doc:from_json_obj(Doc)}
+ end;
+open_doc(Db, DocId) ->
+ couch_db:open_doc(Db, DocId).
+
open_db(Props, UserCtx, ProxyParams) ->
open_db(Props, UserCtx, ProxyParams, false).