diff options
-rw-r--r-- | share/www/script/test/replication.js | 94 | ||||
-rw-r--r-- | src/couchdb/couch_rep.erl | 45 |
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). |