You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2010/09/28 18:49:58 UTC

svn commit: r1002259 - in /couchdb/trunk: share/www/script/test/replication.js src/couchdb/couch_rep.erl

Author: fdmanana
Date: Tue Sep 28 16:49:58 2010
New Revision: 1002259

URL: http://svn.apache.org/viewvc?rev=1002259&view=rev
Log:
Replicator fix: for a filtered replication, the replication ID should take into account the filter function's code and not only its name.

Closes COUCHDB-892.

Modified:
    couchdb/trunk/share/www/script/test/replication.js
    couchdb/trunk/src/couchdb/couch_rep.erl

Modified: couchdb/trunk/share/www/script/test/replication.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/replication.js?rev=1002259&r1=1002258&r2=1002259&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/replication.js (original)
+++ couchdb/trunk/share/www/script/test/replication.js Tue Sep 28 16:49:58 2010
@@ -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);
   }
 
 };

Modified: couchdb/trunk/src/couchdb/couch_rep.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_rep.erl?rev=1002259&r1=1002258&r2=1002259&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_rep.erl (original)
+++ couchdb/trunk/src/couchdb/couch_rep.erl Tue Sep 28 16:49:58 2010
@@ -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
             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).