You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by cm...@apache.org on 2008/08/31 11:43:42 UTC

svn commit: r690668 [1/2] - in /incubator/couchdb/trunk: ./ share/www/script/ src/couchdb/ src/mochiweb/

Author: cmlenz
Date: Sun Aug 31 02:43:41 2008
New Revision: 690668

URL: http://svn.apache.org/viewvc?rev=690668&view=rev
Log:
Merged json_term_changes branch back into trunk.

Removed:
    incubator/couchdb/trunk/src/couchdb/cjson.erl
Modified:
    incubator/couchdb/trunk/   (props changed)
    incubator/couchdb/trunk/share/www/script/couch_tests.js
    incubator/couchdb/trunk/src/couchdb/Makefile.am
    incubator/couchdb/trunk/src/couchdb/couch.app.tpl.in
    incubator/couchdb/trunk/src/couchdb/couch_db.erl
    incubator/couchdb/trunk/src/couchdb/couch_db.hrl
    incubator/couchdb/trunk/src/couchdb/couch_db_update_notifier.erl
    incubator/couchdb/trunk/src/couchdb/couch_db_updater.erl
    incubator/couchdb/trunk/src/couchdb/couch_doc.erl
    incubator/couchdb/trunk/src/couchdb/couch_erl_driver.c
    incubator/couchdb/trunk/src/couchdb/couch_httpd.erl
    incubator/couchdb/trunk/src/couchdb/couch_query_servers.erl
    incubator/couchdb/trunk/src/couchdb/couch_rep.erl
    incubator/couchdb/trunk/src/couchdb/couch_server.erl
    incubator/couchdb/trunk/src/couchdb/couch_util.erl
    incubator/couchdb/trunk/src/couchdb/couch_view.erl
    incubator/couchdb/trunk/src/mochiweb/mochijson2.erl

Propchange: incubator/couchdb/trunk/
            ('svnmerge-integrated' removed)

Modified: incubator/couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/share/www/script/couch_tests.js?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ incubator/couchdb/trunk/share/www/script/couch_tests.js [utf-8] Sun Aug 31 02:43:41 2008
@@ -14,6 +14,9 @@
 
   // Do some basic tests.
   basics: function(debug) {
+    var result = JSON.parse(CouchDB.request("GET", "/").responseText);
+    T(result.couchdb == "Welcome"); 
+    
     var db = new CouchDB("test_suite_db");
     db.deleteDb();
 
@@ -144,7 +147,7 @@
 
     // COPY with existing target
     T(db.save({_id:"doc_to_be_copied",v:1}).ok);
-    var doc = db.save({_id:"doc_to_be_overwritten",v:1});
+    var doc = db.save({_id:"doc_to_be_overwritten",v:2});
     T(doc.ok);
 
     // error condition
@@ -159,9 +162,9 @@
     });
     T(xhr.status == 201);
 
-    var newRev = db.open("doc_to_be_overwritten")._rev;
-    T(rev != newRev);
-
+    var over = db.open("doc_to_be_overwritten");
+    T(rev != over._rev);
+    T(over.v == 1);
   },
 
   // Do some edit conflict detection tests
@@ -311,6 +314,15 @@
     for (i = 0; i < 5; i++) {
       T(db.open(docs[i]._id) == null);
     }
+    
+    // verify creating a document with no id returns a new id
+    var req = CouchDB.request("POST", "/test_suite_db/_bulk_docs", {
+      body: JSON.stringify({"docs": [{"foo":"bar"}]})
+    });
+    result = JSON.parse(req.responseText);
+    
+    T(result.new_revs[0].id != "");
+    T(result.new_revs[0].rev != "");
   },
 
   // test saving a semi-large quanitity of documents and do some view queries.
@@ -815,8 +827,11 @@
 
     T(db.bulkSave(makeDocs(1, numDocs + 1)).ok);
 
+    // test that the _all_docs view returns correctly with keys
+    var results = db.allDocs({startkey:"_design%2F", endkey:"_design%2FZZZ"});
+    T(results.rows.length == 1);
+
     for (var loop = 0; loop < 2; loop++) {
-      if (db.view("test/all_docs") == null) throw "fuck";
       var rows = db.view("test/all_docs").rows;
       for (var i = 0; i < numDocs; i++) {
         T(rows[2*i].key == i+1);
@@ -825,8 +840,19 @@
       T(db.view("test/no_docs").total_rows == 0)
       T(db.view("test/single_doc").total_rows == 1)
       restartServer();
-    }
-
+    };
+    
+    // test when language not specified, Javascript is implied
+    var designDoc2 = {
+      _id:"_design/test2",
+      // language: "javascript", 
+      views: {
+        single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"}
+      }
+    };
+    
+    T(db.save(designDoc2).ok);
+    T(db.view("test2/single_doc").total_rows == 1);
 
     var summate = function(N) {return (N+1)*N/2;};
     var result = db.view("test/summate");

Modified: incubator/couchdb/trunk/src/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/Makefile.am?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/Makefile.am (original)
+++ incubator/couchdb/trunk/src/couchdb/Makefile.am Sun Aug 31 02:43:41 2008
@@ -37,7 +37,6 @@
 CLEANFILES = $(compiled_files) $(doc_base) $(doc_modules) edoc-info
 
 source_files = \
-    cjson.erl \
     couch_btree.erl \
     couch_config.erl \
     couch_config_writer.erl \
@@ -61,7 +60,6 @@
     couch_db_updater.erl
 
 compiled_files = \
-    cjson.beam \
     couch.app \
     couch_btree.beam \
     couch_config.beam \
@@ -94,7 +92,6 @@
     stylesheet.css
 
 doc_modules = \
-    cjson.html \
     couch_btree.html \
     couch_config.html \
     couch_config_writer.html \

Modified: incubator/couchdb/trunk/src/couchdb/couch.app.tpl.in
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch.app.tpl.in?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch.app.tpl.in (original)
+++ incubator/couchdb/trunk/src/couchdb/couch.app.tpl.in Sun Aug 31 02:43:41 2008
@@ -2,7 +2,6 @@
              [{description,"@package_name@"},
               {vsn,"@version@"},
               {modules,[couch_btree,
-                        cjson,
                         couch_db,
                         couch_db_updater,
                         couch_doc,

Modified: incubator/couchdb/trunk/src/couchdb/couch_db.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_db.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_db.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_db.erl Sun Aug 31 02:43:41 2008
@@ -228,11 +228,11 @@
     Docs2 = lists:map(
         fun(#doc{id=Id,revs=Revs}=Doc) ->
             case Id of
-            ?LOCAL_DOC_PREFIX ++ _ ->
-                Rev = case Revs of [] -> 0; [Rev0|_] -> list_to_integer(Rev0) end,
-                Doc#doc{revs=[integer_to_list(Rev + 1)]};
+            <<?LOCAL_DOC_PREFIX, _/binary>> ->
+                Rev = case Revs of [] -> 0; [Rev0|_] -> list_to_integer(binary_to_list(Rev0)) end,
+                Doc#doc{revs=[list_to_binary(integer_to_list(Rev + 1))]};
             _ ->
-                Doc#doc{revs=[integer_to_list(couch_util:rand32()) | Revs]}
+                Doc#doc{revs=[list_to_binary(integer_to_list(couch_util:rand32())) | Revs]}
             end
         end, Docs),
     NewRevs = [NewRev || #doc{revs=[NewRev|_]} <- Docs2],
@@ -429,10 +429,10 @@
         end,
         IdRevs, LookupResults).
 
-open_doc_int(Db, ?LOCAL_DOC_PREFIX ++ _ = Id, _Options) ->
+open_doc_int(Db, <<?LOCAL_DOC_PREFIX, _/binary>> = Id, _Options) ->
     case couch_btree:lookup(Db#db.local_docs_btree, [Id]) of
     [{ok, {_, {Rev, BodyData}}}] ->
-        {ok, #doc{id=Id, revs=[integer_to_list(Rev)], body=BodyData}};
+        {ok, #doc{id=Id, revs=[list_to_binary(integer_to_list(Rev))], body=BodyData}};
     [not_found] ->
         {not_found, missing}
     end;

Modified: incubator/couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_db.hrl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_db.hrl Sun Aug 31 02:43:41 2008
@@ -14,7 +14,10 @@
 -define(DESIGN_DOC_PREFIX0, "_design").
 -define(DESIGN_DOC_PREFIX, "_design/").
 
--define(DEFAULT_ATTACHMENT_CONTENT_TYPE, "application/octet-stream").
+-define(JSON_ENCODE(V), mochijson2:encode(V)).
+-define(JSON_DECODE(V), mochijson2:decode(V)).
+
+-define(DEFAULT_ATTACHMENT_CONTENT_TYPE, <<"application/octet-stream">>).
         
 -define(LOG_DEBUG(Format, Args),
     case couch_log:debug_on() of
@@ -33,8 +36,8 @@
 
 -record(doc_info,
     {
-    id = "",
-    rev = "",
+    id = <<"">>,
+    rev = <<"">>,
     update_seq = 0,
     summary_pointer = nil,
     conflict_revs = [],
@@ -43,7 +46,7 @@
     }).
 
 -record(full_doc_info,
-    {id = "",
+    {id = <<"">>,
     update_seq = 0,
     deleted = false,
     rev_tree = []
@@ -51,11 +54,11 @@
 
 -record(doc,
     {
-    id = "",
+    id = <<"">>,
     revs = [],
 
     % the json body object.
-    body = {obj, []},
+    body = {[]},
 
     % each attachment contains:
     %    {data, Type, <<binary>>}

Modified: incubator/couchdb/trunk/src/couchdb/couch_db_update_notifier.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_db_update_notifier.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_db_update_notifier.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_db_update_notifier.erl Sun Aug 31 02:43:41 2008
@@ -25,6 +25,8 @@
 -export([start_link/1, notify/1]).
 -export([init/1, terminate/2, handle_event/2, handle_call/2, handle_info/2, code_change/3,stop/1]).
 
+-include("couch_db.hrl").
+
 start_link(Exec) ->
     couch_event_sup:start_link(couch_db_update, {couch_db_update_notifier, make_ref()}, Exec).
 
@@ -50,8 +52,8 @@
     FunAcc2 = Fun(Event, FunAcc),
     {ok, {Fun, FunAcc2}};
 handle_event({EventAtom, DbName}, Port) ->
-    Obj = {obj, [{type, atom_to_list(EventAtom)}, {db, DbName}]},
-    true = port_command(Port, cjson:encode(Obj) ++ "\n"),
+    Obj = {[{type, atom_to_list(EventAtom)}, {db, DbName}]},
+    true = port_command(Port, ?JSON_ENCODE(Obj) ++ "\n"),
     {ok, Port}.
 
 handle_call(_Request, State) ->

Modified: incubator/couchdb/trunk/src/couchdb/couch_db_updater.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_db_updater.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_db_updater.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_db_updater.erl Sun Aug 31 02:43:41 2008
@@ -96,7 +96,7 @@
         file:delete(Filepath ++ ".old"),
             
         ok = gen_server:call(Db#db.main_pid, {db_updated, NewDb2}),
-        ?LOG_INFO("Compaction for db ~p completed.", [Db#db.name]),
+        ?LOG_INFO("Compaction for db \"~s\" completed.", [Db#db.name]),
         {noreply, NewDb2#db{compactor_pid=nil}};
     false ->
         ?LOG_INFO("Compaction file still behind main file "
@@ -287,7 +287,7 @@
     {DocsList2, NonRepDocs} = lists:foldl(
         fun([#doc{id=Id}=Doc | Rest]=Docs, {DocsListAcc, NonRepDocsAcc}) ->
             case Id of
-            ?LOCAL_DOC_PREFIX ++ _ when Rest==[] ->
+            <<?LOCAL_DOC_PREFIX, _/binary>> when Rest==[] ->
                 % when saving NR (non rep) documents, you can only save a single rev
                 {DocsListAcc, [Doc | NonRepDocsAcc]};
             Id->
@@ -363,7 +363,7 @@
             NewRev =
             case Revs of
                 [] -> 0;
-                [RevStr|_] -> list_to_integer(RevStr)
+                [RevStr|_] -> list_to_integer(binary_to_list(RevStr))
             end,
             OldRev =
             case OldDocLookup of

Modified: incubator/couchdb/trunk/src/couchdb/couch_doc.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_doc.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_doc.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_doc.erl Sun Aug 31 02:43:41 2008
@@ -19,109 +19,116 @@
 -include("couch_db.hrl").
 
 to_json_obj(#doc{id=Id,deleted=Del,body=Body,revs=Revs,meta=Meta}=Doc,Options)->
-    {obj, [{"_id", Id}] ++
+    {[{<<"_id">>, Id}] ++
         case Revs of
         [] -> [];
-        _ -> [{"_rev", lists:nth(1, Revs)}]
+        _ -> [{<<"_rev">>, lists:nth(1, Revs)}]
         end ++
         case Del of
         false ->
-            {obj, BodyProps} = Body,
+            {BodyProps} = Body,
             BodyProps;
         true ->
-            [{"_deleted", true}]
+            [{<<"_deleted">>, true}]
         end ++
         case lists:member(revs, Options) of
         false -> [];
         true ->
-            [{"_revs", list_to_tuple(Revs)}]
+            [{<<"_revs">>, Revs}]
         end ++
         lists:map(
             fun({revs_info, RevsInfo}) ->
                 JsonRevsInfo =
-                [{obj, [{rev, Rev}, {status, atom_to_list(Status)}]} ||
+                [{[{rev, Rev}, {status, atom_to_list(Status)}]} ||
                     {Rev, Status} <- RevsInfo],
-                {"_revs_info", list_to_tuple(JsonRevsInfo)};
+                {<<"_revs_info">>, JsonRevsInfo};
             ({conflicts, Conflicts}) ->
-                {"_conflicts", list_to_tuple(Conflicts)};
+                {<<"_conflicts">>, Conflicts};
             ({deleted_conflicts, Conflicts}) ->
-                {"_deleted_conflicts", list_to_tuple(Conflicts)}
+                {<<"_deleted_conflicts">>, Conflicts}
             end, Meta) ++
         case lists:member(attachments, Options) of
         true -> % return the full rev list and the binaries as strings.
             BinProps = lists:map(
                 fun({Name, {Type, BinValue}}) ->
-                    {Name, {obj, [
-                        {"content_type", Type},
-                        {"data", couch_util:encodeBase64(bin_to_binary(BinValue))}
+                    {Name, {[
+                        {<<"content_type">>, Type},
+                        {<<"data">>, couch_util:encodeBase64(bin_to_binary(BinValue))}
                     ]}}
                 end,
                 Doc#doc.attachments),
             case BinProps of
             [] -> [];
-            _ -> [{"_attachments", {obj, BinProps}}]
+            _ -> [{<<"_attachments">>, {BinProps}}]
             end;
         false ->
             BinProps = lists:map(
                 fun({Name, {Type, BinValue}}) ->
-                    {Name, {obj, [
-                        {"stub", true},
-                        {"content_type", Type},
-                        {"length", bin_size(BinValue)}
+                    {Name, {[
+                        {<<"stub">>, true},
+                        {<<"content_type">>, Type},
+                        {<<"length">>, bin_size(BinValue)}
                     ]}}
                 end,
                 Doc#doc.attachments),
             case BinProps of
                 [] -> [];
-                _ -> [{"_attachments", {obj, BinProps}}]
+                _ -> [{<<"_attachments">>, {BinProps}}]
             end
         end
         }.
 
-from_json_obj({obj, Props}) ->
-    {obj,JsonBins} = proplists:get_value("_attachments", Props, {obj, []}),
-    Bins = lists:flatmap(fun({Name, {obj, BinProps}}) ->
-        case proplists:get_value("stub", BinProps) of
+from_json_obj({Props}) ->
+    {JsonBins} = proplists:get_value(<<"_attachments">>, Props, {[]}),
+    Bins = lists:flatmap(fun({Name, {BinProps}}) ->
+        case proplists:get_value(<<"stub">>, BinProps) of
         true ->
             [{Name, stub}];
         _ ->
-            Value = proplists:get_value("data", BinProps),
-            Type = proplists:get_value("content_type", BinProps,
+            Value = proplists:get_value(<<"data">>, BinProps),
+            Type = proplists:get_value(<<"content_type">>, BinProps,
                     ?DEFAULT_ATTACHMENT_CONTENT_TYPE),
             [{Name, {Type, couch_util:decodeBase64(Value)}}]
         end
     end, JsonBins),
-    AllowedSpecialMembers = ["id", "revs", "rev", "attachments", "revs_info",
-        "conflicts", "deleted_conflicts", "deleted"],
+    AllowedSpecialMembers = [<<"id">>, <<"revs">>, <<"rev">>, <<"attachments">>, <<"revs_info">>,
+        <<"conflicts">>, <<"deleted_conflicts">>, <<"deleted">>],
+    % collect all the doc-members that start with "_"
+    % if any aren't in the AllowedSpecialMembers list 
+    % then throw a doc_validation error
     [case lists:member(Name, AllowedSpecialMembers) of
         true ->
             ok;
         false ->
             throw({doc_validation, io_lib:format("Bad special document member: _~s", [Name])})
         end
-         || {[$_|Name], _Value} <- Props],
+         || {<<$_,Name/binary>>, _Value} <- Props],
     Revs =
-    case tuple_to_list(proplists:get_value("_revs", Props, {})) of
+    case proplists:get_value(<<"_revs">>, Props, []) of
     [] ->
-        case proplists:get_value("_rev", Props) of
+        case proplists:get_value(<<"_rev">>, Props) of
         undefined -> [];
         Rev -> [Rev]
         end;
     Revs0 ->
         Revs0
     end,
-    case proplists:get_value("_id", Props, "") of
-    Id when is_list(Id) ->
-        #doc{
-            id = Id,
-            revs = Revs,
-            deleted = proplists:get_value("_deleted", Props, false),
-            body = {obj, [{Key, Value} || {[FirstChar|_]=Key, Value} <- Props, FirstChar /= $_]},
-            attachments = Bins
-            };
-    _ ->
+    case proplists:get_value(<<"_id">>, Props, <<>>) of
+    Id when is_binary(Id) -> ok;
+    Id ->
+        ?LOG_DEBUG("Document id is not a string: ~p", [Id]),
         throw({invalid_document_id, "Document id is not a string"})
-    end.
+    end,
+    
+    % strip out the all props beginning with _
+    NewBody = {[{K, V} || {<<First,_/binary>>=K, V} <- Props, First /= $_]},
+    #doc{
+        id = Id,
+        revs = Revs,
+        deleted = proplists:get_value(<<"_deleted">>, Props, false),
+        body = NewBody,
+        attachments = Bins
+        }.
 
 
 to_doc_info(#full_doc_info{id=Id,update_seq=Seq,rev_tree=Tree}) ->
@@ -181,9 +188,9 @@
     {ok, Bin, _Sp2} = couch_stream:read(Fd, Sp, Len),
     Bin.
 
-get_view_functions(#doc{body={obj, Fields}}) ->
-    Lang = proplists:get_value("language", Fields, "javascript"),
-    {obj, Views} = proplists:get_value("views", Fields, {obj, []}),
+get_view_functions(#doc{body={Fields}}) ->
+    Lang = proplists:get_value(<<"language">>, Fields, <<"javascript">>),
+    {Views} = proplists:get_value(<<"views">>, Fields, {[]}),
     {Lang, [{ViewName, Value} || {ViewName, Value} <- Views, is_list(Value)]};
 get_view_functions(_Doc) ->
     none.

Modified: incubator/couchdb/trunk/src/couchdb/couch_erl_driver.c
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_erl_driver.c?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_erl_driver.c (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_erl_driver.c Sun Aug 31 02:43:41 2008
@@ -131,9 +131,9 @@
         if (collResult < 0)
           response = 0; //lt
         else if (collResult > 0)
-          response = 1; //gt
+          response = 2; //gt
         else
-          response = 2; //eq
+          response = 1; //eq
 
         return return_control_result(&response, sizeof(response), rbuf, rlen);
         }

Modified: incubator/couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_httpd.erl Sun Aug 31 02:43:41 2008
@@ -26,14 +26,14 @@
 
 -record(view_query_args, {
     start_key = nil,
-    end_key = <<>>,
+    end_key = {},
     count = 10000000000, % a huge huge default number. Picked so we don't have
                          % to do different logic for when there is no count
                          % limit
     update = true,
     direction = fwd,
     start_docid = nil,
-    end_docid = <<>>,
+    end_docid = {},
     skip = 0,
     group_level = 0
 }).
@@ -91,7 +91,7 @@
     {Path, _, _} = mochiweb_util:urlsplit_path(Req:get(raw_path)),
 
     ?LOG_DEBUG("~p ~s ~p~nHeaders: ~p", [
-        Method,
+        Req:get(method),
         Path,
         Req:get(version),
         mochiweb_headers:to_list(Req:get(headers))
@@ -148,9 +148,9 @@
 % Global request handlers
 
 handle_welcome_request(Req, 'GET') ->
-    send_json(Req, {obj, [
-        {"couchdb", "Welcome"},
-        {"version", couch_server:get_version()}
+    send_json(Req, {[
+        {couchdb, <<"Welcome">>},
+        {version, list_to_binary(couch_server:get_version())}
     ]});
 
 handle_welcome_request(_Req, _Method) ->
@@ -158,24 +158,24 @@
 
 handle_all_dbs_request(Req, 'GET') ->
     {ok, DbNames} = couch_server:all_databases(),
-    send_json(Req, list_to_tuple(DbNames));
+    send_json(Req, DbNames);
 
 handle_all_dbs_request(_Req, _Method) ->
     throw({method_not_allowed, "GET,HEAD"}).
 
 handle_replicate_request(Req, 'POST') ->
-    {obj, Props} = cjson:decode(Req:recv_body()),
-    Source = proplists:get_value("source", Props),
-    Target = proplists:get_value("target", Props),
-    {obj, Options} = proplists:get_value("options", Props, {obj, []}),
-    {ok, {obj, JsonResults}} = couch_rep:replicate(Source, Target, Options),
-    send_json(Req, {obj, [{ok, true} | JsonResults]});
+    {Props} = ?JSON_DECODE(Req:recv_body()),
+    Source = proplists:get_value(<<"source">>, Props),
+    Target = proplists:get_value(<<"target">>, Props),
+    {Options} = proplists:get_value(<<"options">>, Props, {[]}),
+    {ok, {JsonResults}} = couch_rep:replicate(Source, Target, Options),
+    send_json(Req, {[{ok, true} | JsonResults]});
 
 handle_replicate_request(_Req, _Method) ->
     throw({method_not_allowed, "POST"}).
 
 handle_restart_request(Req, 'POST') ->
-    Response = send_json(Req, {obj, [{ok, true}]}),
+    Response = send_json(Req, {[{ok, true}]}),
     spawn(fun() -> couch_server:remote_restart() end),
     Response;
 
@@ -187,7 +187,7 @@
     % generate the uuids
     UUIDs = [ couch_util:new_uuid() || _ <- lists:seq(1,Count)],
     % send a JSON response
-    send_json(Req, {obj, [{"uuids", list_to_tuple(UUIDs)}]});
+    send_json(Req, {[{"uuids", UUIDs}]});
 
 handle_uuids_request(_Req, _Method) ->
     throw({method_not_allowed, "POST"}).
@@ -197,14 +197,15 @@
 
 handle_db_request(Req, Method, {Path}) ->
     UriParts = string:tokens(Path, "/"),
-    [DbName|Rest] = UriParts,
-    handle_db_request(Req, Method, {mochiweb_util:unquote(DbName), Rest});
+    [DbName|Rest] =
+        [list_to_binary(mochiweb_util:unquote(Part)) || Part <- UriParts],
+    handle_db_request(Req, Method, {DbName, Rest});
 
 handle_db_request(Req, 'PUT', {DbName, []}) ->
     case couch_server:create(DbName, []) of
         {ok, Db} ->
             couch_db:close(Db),
-            send_json(Req, 201, {obj, [{ok, true}]});
+            send_json(Req, 201, {[{ok, true}]});
         {error, database_already_exists} ->
             Msg = io_lib:format("Database ~p already exists.", [DbName]),
             throw({database_already_exists, Msg});
@@ -216,7 +217,7 @@
 handle_db_request(Req, 'DELETE', {DbName, []}) ->
     case couch_server:delete(DbName) of
     ok ->
-        send_json(Req, 200, {obj, [
+        send_json(Req, 200, {[
             {ok, true}
         ]});
     Error ->
@@ -237,15 +238,15 @@
 
 handle_db_request(Req, 'GET', {DbName, Db, []}) ->
     {ok, DbInfo} = couch_db:get_db_info(Db),
-    send_json(Req, {obj, [{db_name, DbName} | DbInfo]});
+    send_json(Req, {[{db_name, DbName} | DbInfo]});
 
 handle_db_request(Req, 'POST', {_DbName, Db, []}) ->
     % TODO: Etag handling
-    Json = cjson:decode(Req:recv_body(?MAX_DOC_SIZE)),
+    Json = ?JSON_DECODE(Req:recv_body(?MAX_DOC_SIZE)),
     Doc = couch_doc:from_json_obj(Json),
     DocId = couch_util:new_uuid(),
     {ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=[]}, []),
-    send_json(Req, 201, {obj, [
+    send_json(Req, 201, {[
         {ok, true},
         {id, DocId},
         {rev, NewRev}
@@ -254,63 +255,63 @@
 handle_db_request(_Req, _Method, {_DbName, _Db, []}) ->
     throw({method_not_allowed, "DELETE,GET,HEAD,POST"});
 
-handle_db_request(Req, 'POST', {_DbName, Db, ["_bulk_docs"]}) ->
+handle_db_request(Req, 'POST', {_DbName, Db, [<<"_bulk_docs">>]}) ->
     Options = [], % put options here.
-    {obj, JsonProps} = cjson:decode(Req:recv_body(?MAX_DOC_SIZE)),
-    DocsArray = proplists:get_value("docs", JsonProps),
+    {JsonProps} = ?JSON_DECODE(Req:recv_body(?MAX_DOC_SIZE)),
+    DocsArray = proplists:get_value(<<"docs">>, JsonProps),
     % convert all the doc elements to native docs
-    case proplists:get_value("new_edits", JsonProps, true) of
+    case proplists:get_value(<<"new_edits">>, JsonProps, true) of
     true ->
         Docs = lists:map(
-            fun({obj, ObjProps} = JsonObj) ->
+            fun({ObjProps} = JsonObj) ->
                 Doc = couch_doc:from_json_obj(JsonObj),
                 Id = case Doc#doc.id of
-                    "" -> couch_util:new_uuid();
+                    <<>> -> couch_util:new_uuid();
                     Id0 -> Id0
                 end,
-                Revs = case proplists:get_value("_rev", ObjProps) of
+                Revs = case proplists:get_value(<<"_rev">>, ObjProps) of
                     undefined -> [];
                     Rev  -> [Rev]
                 end,
                 Doc#doc{id=Id,revs=Revs}
             end,
-            tuple_to_list(DocsArray)),
+            DocsArray),
         {ok, ResultRevs} = couch_db:update_docs(Db, Docs, Options),
 
         % output the results
         DocResults = lists:zipwith(
             fun(Doc, NewRev) ->
-                {obj, [{"id", Doc#doc.id}, {"rev", NewRev}]}
+                {[{"id", Doc#doc.id}, {"rev", NewRev}]}
             end,
             Docs, ResultRevs),
-        send_json(Req, 201, {obj, [
+        send_json(Req, 201, {[
             {ok, true},
-            {new_revs, list_to_tuple(DocResults)}
+            {new_revs, DocResults}
         ]});
 
     false ->
-        Docs = [couch_doc:from_json_obj(JsonObj) || JsonObj <- tuple_to_list(DocsArray)],
+        Docs = [couch_doc:from_json_obj(JsonObj) || JsonObj <- DocsArray],
         ok = couch_db:save_docs(Db, Docs, Options),
-        send_json(Req, 201, {obj, [
+        send_json(Req, 201, {[
             {ok, true}
         ]})
     end;
 
-handle_db_request(_Req, _Method, {_DbName, _Db, ["_bulk_docs"]}) ->
+handle_db_request(_Req, _Method, {_DbName, _Db, [<<"_bulk_docs">>]}) ->
     throw({method_not_allowed, "POST"});
 
-handle_db_request(Req, 'POST', {_DbName, Db, ["_compact"]}) ->
+handle_db_request(Req, 'POST', {_DbName, Db, [<<"_compact">>]}) ->
     ok = couch_db:start_compact(Db),
-    send_json(Req, 202, {obj, [
+    send_json(Req, 202, {[
         {ok, true}
     ]});
 
-handle_db_request(_Req, _Method, {_DbName, _Db, ["_compact"]}) ->
+handle_db_request(_Req, _Method, {_DbName, _Db, [<<"_compact">>]}) ->
     throw({method_not_allowed, "POST"});
 
 % View request handlers
 
-handle_db_request(Req, 'GET', {_DbName, Db, ["_all_docs"]}) ->
+handle_db_request(Req, 'GET', {_DbName, Db, [<<"_all_docs">>]}) ->
     #view_query_args{
         start_key = StartKey,
         start_docid = StartDocId,
@@ -321,7 +322,7 @@
     {ok, Info} = couch_db:get_db_info(Db),
     TotalRowCount = proplists:get_value(doc_count, Info),
 
-    StartId = if is_list(StartKey) -> StartKey;
+    StartId = if is_binary(StartKey) -> StartKey;
     true -> StartDocId
     end,
 
@@ -330,7 +331,7 @@
     AdapterFun = fun(#full_doc_info{id=Id}=FullDocInfo, Offset, Acc) ->
         case couch_doc:to_doc_info(FullDocInfo) of
         #doc_info{deleted=false, rev=Rev} ->
-            FoldlFun({{Id, Id}, {obj, [{rev, Rev}]}}, Offset, Acc);
+            FoldlFun({{Id, Id}, {[{rev, Rev}]}}, Offset, Acc);
         #doc_info{deleted=true} ->
             {ok, Acc}
         end
@@ -339,10 +340,10 @@
             {Count, SkipCount, undefined, []}),
     finish_view_fold(Req, TotalRowCount, {ok, FoldResult});
 
-handle_db_request(_Req, _Method, {_DbName, _Db, ["_all_docs"]}) ->
+handle_db_request(_Req, _Method, {_DbName, _Db, [<<"_all_docs">>]}) ->
     throw({method_not_allowed, "GET,HEAD"});
 
-handle_db_request(Req, 'GET', {_DbName, Db, ["_all_docs_by_seq"]}) ->
+handle_db_request(Req, 'GET', {_DbName, Db, [<<"_all_docs_by_seq">>]}) ->
     #view_query_args{
         start_key = StartKey,
         count = Count,
@@ -370,15 +371,15 @@
                 conflict_revs=ConflictRevs,
                 deleted_conflict_revs=DelConflictRevs
             } = DocInfo,
-            Json = {obj,
+            Json = {
                 [{"rev", Rev}] ++
                 case ConflictRevs of
                     []  ->  [];
-                    _   ->  [{"conflicts", list_to_tuple(ConflictRevs)}]
+                    _   ->  [{"conflicts", ConflictRevs}]
                 end ++
                 case DelConflictRevs of
                     []  ->  [];
-                    _   ->  [{"deleted_conflicts", list_to_tuple(DelConflictRevs)}]
+                    _   ->  [{"deleted_conflicts", DelConflictRevs}]
                 end ++
                 case Deleted of
                     true -> [{"deleted", true}];
@@ -392,7 +393,7 @@
 handle_db_request(_Req, _Method, {_DbName, _Db, ["_all_docs_by_seq"]}) ->
     throw({method_not_allowed, "GET,HEAD"});
 
-handle_db_request(Req, 'GET', {DbName, _Db, ["_view", DocId, ViewName]}) ->
+handle_db_request(Req, 'GET', {DbName, _Db, [<<"_view">>, DocId, ViewName]}) ->
     #view_query_args{
         start_key = StartKey,
         count = Count,
@@ -400,8 +401,9 @@
         direction = Dir,
         start_docid = StartDocId
     } = QueryArgs = parse_view_query(Req),
-    case couch_view:get_map_view({DbName, "_design/" ++ DocId, ViewName}) of
-    {ok, View} ->
+    
+    case couch_view:get_map_view({DbName, <<"_design/", DocId/binary>>, ViewName}) of
+    {ok, View} ->    
         {ok, RowCount} = couch_view:get_row_count(View),
         Start = {StartKey, StartDocId},
         FoldlFun = make_view_fold_fun(Req, QueryArgs, RowCount,
@@ -410,7 +412,7 @@
         FoldResult = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
         finish_view_fold(Req, RowCount, FoldResult);
     {not_found, Reason} ->
-        case couch_view:get_reduce_view({DbName, "_design/" ++ DocId, ViewName}) of
+        case couch_view:get_reduce_view({DbName, <<"_design/", DocId/binary>>, ViewName}) of
         {ok, View} ->
             output_reduce_view(Req, View);
         _ ->
@@ -418,27 +420,25 @@
         end
     end;
 
-handle_db_request(_Req, _Method, {_DbName, _Db, ["_view", _DocId, _ViewName]}) ->
+handle_db_request(_Req, _Method, {_DbName, _Db, [<<"_view">>, _DocId, _ViewName]}) ->
     throw({method_not_allowed, "GET,HEAD"});
 
-handle_db_request(Req, 'POST', {_DbName, Db, ["_missing_revs"]}) ->
-    {obj, JsonDocIdRevs} = cjson:decode(Req:recv_body()),
-    DocIdRevs = [{Id, tuple_to_list(Revs)} || {Id, Revs} <- JsonDocIdRevs],
-    {ok, Results} = couch_db:get_missing_revs(Db, DocIdRevs),
-    JsonResults = [{Id, list_to_tuple(Revs)} || {Id, Revs} <- Results],
-    send_json(Req, {obj, [
-        {missing_revs, {obj, JsonResults}}
+handle_db_request(Req, 'POST', {_DbName, Db, [<<"_missing_revs">>]}) ->
+    {JsonDocIdRevs} = ?JSON_DECODE(Req:recv_body()),
+    {ok, Results} = couch_db:get_missing_revs(Db, JsonDocIdRevs),
+    send_json(Req, {[
+        {missing_revs, {Results}}
     ]});
 
-handle_db_request(Req, 'POST', {_DbName, Db, ["_increment_update_seq"]}) ->
+handle_db_request(Req, 'POST', {_DbName, Db, [<<"_increment_update_seq">>]}) ->
     % NOTE, use at own risk. This functionality is experimental
     % and might go away entirely.
     {ok, NewSeq} = couch_db:increment_update_seq(Db),
-    send_json(Req, {obj, [{ok, true},
+    send_json(Req, {[{ok, true},
         {update_seq, NewSeq}
     ]});
 
-handle_db_request(Req, 'POST', {DbName, _Db, ["_temp_view"]}) ->
+handle_db_request(Req, 'POST', {DbName, _Db, [<<"_temp_view">>]}) ->
     #view_query_args{
         start_key = StartKey,
         count = Count,
@@ -452,18 +452,22 @@
         "application/json" -> ok;
         Else -> throw({incorrect_mime_type, Else})
     end,
-    {obj, Props} = cjson:decode(Req:recv_body()),
-    Language = proplists:get_value("language", Props, "javascript"),
-    MapSrc = proplists:get_value("map", Props),
-    case proplists:get_value("reduce", Props, null) of
+    {Props} = ?JSON_DECODE(Req:recv_body()),    
+    Language = proplists:get_value(<<"language">>, Props, <<"javascript">>),
+    MapSrc = proplists:get_value(<<"map">>, Props),
+    case proplists:get_value(<<"reduce">>, Props, null) of
     null ->
         {ok, View} = couch_view:get_map_view({temp, DbName, Language, MapSrc}),
         Start = {StartKey, StartDocId},
+        
         {ok, TotalRows} = couch_view:get_row_count(View),
+        
         FoldlFun = make_view_fold_fun(Req, QueryArgs, TotalRows,
                 fun couch_view:reduce_to_count/1),
         FoldAccInit = {Count, SkipCount, undefined, []},
-        FoldResult = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
+        FoldResult = couch_view:fold(View, Start, Dir, fun(A, B, C) ->
+            FoldlFun(A, B, C)
+        end, FoldAccInit),
         finish_view_fold(Req, TotalRows, FoldResult);
 
     RedSrc ->
@@ -472,26 +476,22 @@
         output_reduce_view(Req, View)
     end;
 
-handle_db_request(_Req, _Method, {_DbName, _Db, ["_temp_view"]}) ->
+handle_db_request(_Req, _Method, {_DbName, _Db, [<<"_temp_view">>]}) ->
     throw({method_not_allowed, "POST"});
 
 % Document request handlers
 
-handle_db_request(Req, Method, {DbName, Db, ["_design", DesignName]}) ->
+handle_db_request(Req, Method, {DbName, Db, [<<"_design">>, Name]}) ->
     % Special case to enable using an unencoded in the URL of design docs, as
     % slashes in document IDs must otherwise be URL encoded
-    DocId = mochiweb_util:join(["_design", DesignName], "/"),
-    handle_db_request(Req, Method, {DbName, Db, [DocId]});
+    handle_db_request(Req, Method, {DbName, Db, [<<"_design/", Name/binary>>]});
 
 handle_db_request(Req, Method, {DbName, Db, [DocId]}) ->
-    UnquotedDocId = mochiweb_util:unquote(DocId),
-    handle_doc_request(Req, Method, DbName, Db, UnquotedDocId);
+    handle_doc_request(Req, Method, DbName, Db,DocId);
 
 handle_db_request(Req, Method, {DbName, Db, [DocId, FileName]}) ->
-    UnquotedDocId = mochiweb_util:unquote(DocId),
-    UnquotedFileName = mochiweb_util:unquote(FileName),
-    handle_attachment_request(Req, Method, DbName, Db, UnquotedDocId,
-                              UnquotedFileName).
+    handle_attachment_request(Req, Method, DbName, Db, DocId,
+                              FileName).
 
 output_reduce_view(Req, View) ->
     #view_query_args{
@@ -508,8 +508,8 @@
         fun({_Key1,_}, {_Key2,_}) when GroupLevel == 0 ->
             true;
         ({Key1,_}, {Key2,_})
-                when is_integer(GroupLevel) and is_tuple(Key1) and is_tuple(Key2) ->
-            lists:sublist(tuple_to_list(Key1), GroupLevel) == lists:sublist(tuple_to_list(Key2), GroupLevel);
+                when is_integer(GroupLevel) and is_list(Key1) and is_list(Key2) ->
+            lists:sublist(Key1, GroupLevel) == lists:sublist(Key2, GroupLevel);
         ({Key1,_}, {Key2,_}) ->
             Key1 == Key2
         end,
@@ -522,52 +522,37 @@
         (_Key, _Red, {AccSeparator,0,AccCount}) when AccCount == 0 ->
             {stop, {AccSeparator,0,AccCount}};
         (_Key, Red, {AccSeparator,0,AccCount}) when GroupLevel == 0 ->
-            Json = lists:flatten(cjson:encode({obj, [{key, null}, {value, Red}]})),
+            Json = ?JSON_ENCODE({[{key, null}, {value, Red}]}),
             Resp:write_chunk(AccSeparator ++ Json),
             {ok, {",",0,AccCount-1}};
         (Key, Red, {AccSeparator,0,AccCount})
-                when is_integer(GroupLevel)
-                andalso is_tuple(Key)
-                andalso element(1, Key) /= obj  ->
-            Json = lists:flatten(cjson:encode(
-                {obj, [{key, list_to_tuple(lists:sublist(tuple_to_list(Key), GroupLevel))},
-                        {value, Red}]})),
+                when is_integer(GroupLevel) 
+                andalso is_list(Key) ->
+            Json = ?JSON_ENCODE(
+                {[{key, lists:sublist(Key, GroupLevel)},{value, Red}]}),
             Resp:write_chunk(AccSeparator ++ Json),
             {ok, {",",0,AccCount-1}};
         (Key, Red, {AccSeparator,0,AccCount}) ->
-            Json = lists:flatten(cjson:encode({obj, [{key, Key}, {value, Red}]})),
+            Json = ?JSON_ENCODE({[{key, Key}, {value, Red}]}),
             Resp:write_chunk(AccSeparator ++ Json),
             {ok, {",",0,AccCount-1}}
         end, {"", Skip, Count}),
     Resp:write_chunk("]}"),
     end_json_response(Resp).
 
+    
 handle_doc_request(Req, 'DELETE', _DbName, Db, DocId) ->
-    QueryRev = proplists:get_value("rev", Req:parse_qs()),
-    Etag = case Req:get_header_value("If-Match") of
-        undefined ->
-            undefined;
-        Tag ->
-            string:strip(Tag, both, $")
-    end,
-    RevToDelete = case {QueryRev, Etag} of
-    {undefined, undefined} ->
-        throw({missing_rev, "Document rev/etag must be specified to delete"});
-    {_, undefined} ->
-        QueryRev;
-    {undefined, _} ->
-        Etag;
-    _ when QueryRev == Etag ->
-        Etag;
-    _ ->
-        throw({bad_request, "Document rev and etag have different values"})
-    end,
-    {ok, NewRev} = couch_db:delete_doc(Db, DocId, [RevToDelete]),
-    send_json(Req, 200, {obj, [
-        {ok, true},
-        {id, DocId},
-        {rev, NewRev}
-    ]});
+    case extract_header_rev(Req, proplists:get_value("rev", Req:parse_qs())) of
+    missing_rev ->
+        {missing_rev, "Document rev/etag must be specified to delete"};
+    RevToDelete ->
+        {ok, NewRev} = couch_db:delete_doc(Db, DocId, [RevToDelete]),
+        send_json(Req, 200, {[
+            {ok, true},
+            {id, DocId},
+            {rev, NewRev}
+            ]})
+    end;
 
 handle_doc_request(Req, 'GET', _DbName, Db, DocId) ->
     #doc_query_args{
@@ -596,10 +581,10 @@
                 case Result of
                 {ok, Doc} ->
                     JsonDoc = couch_doc:to_json_obj(Doc, Options),
-                    Json = lists:flatten(cjson:encode({obj, [{ok, JsonDoc}]})),
+                    Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
                     Resp:write_chunk(AccSeparator ++ Json);
                 {{not_found, missing}, RevId} ->
-                    Json = lists:flatten(cjson:encode({obj, [{"missing", RevId}]})),
+                    Json = ?JSON_ENCODE({[{"missing", RevId}]}),
                     Resp:write_chunk(AccSeparator ++ Json)
                 end,
                 "," % AccSeparator now has a comma
@@ -611,16 +596,17 @@
 
 handle_doc_request(Req, 'POST', _DbName, Db, DocId) ->
     Form = mochiweb_multipart:parse_form(Req),
-    Rev = proplists:get_value("_rev", Form),
-    NewAttachments = [{Name, {ContentType, Content}} ||
-                      {Name, {ContentType, _}, Content} <-
-                      proplists:get_all_values("_attachments", Form)],
-
+    Rev = list_to_binary(proplists:get_value("_rev", Form)),
     Doc = case couch_db:open_doc_revs(Db, DocId, [Rev], []) of
         {ok, [{ok, Doc0}]}  -> Doc0#doc{revs=[Rev]};
         {ok, [Error]}       -> throw(Error)
     end,
 
+    NewAttachments = [
+        {list_to_binary(Name), {list_to_binary(ContentType), Content}} ||
+        {Name, {ContentType, _}, Content} <-
+        proplists:get_all_values("_attachments", Form)
+    ],
     #doc{attachments=Attachments} = Doc,
     NewDoc = Doc#doc{
         attachments = Attachments ++ NewAttachments
@@ -634,87 +620,78 @@
     ]});
 
 handle_doc_request(Req, 'PUT', _DbName, Db, DocId) ->
-    Json = {obj, DocProps} = cjson:decode(Req:recv_body(?MAX_DOC_SIZE)),
-    DocRev = proplists:get_value("_rev", DocProps),
-    Etag = case Req:get_header_value("If-Match") of
-        undefined ->
-            undefined;
-        Tag ->
-            string:strip(Tag, both, $")
-    end,
-    Revs = case {DocRev, Etag} of
-    {undefined, undefined} ->
-        [];
-    {_, undefined} ->
-        [DocRev];
-    {undefined, _} ->
-        [Etag];
-    _ when DocRev == Etag ->
-        [Etag];
-    _ ->
-        throw({bad_request, "Document rev and etag have different values"})
-    end,
-
+    Json = ?JSON_DECODE(Req:recv_body(?MAX_DOC_SIZE)),
     Doc = couch_doc:from_json_obj(Json),
-
+    ExplicitRev =
+    case Doc#doc.revs of
+        [Rev0|_] -> Rev0;
+        [] -> undefined
+    end,
+    case extract_header_rev(Req, ExplicitRev) of
+    missing_rev ->
+        Revs = [];
+    Rev ->
+        Revs = [Rev]
+    end,
     {ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=Revs}, []),
-    send_json(Req, 201, [{"Etag", "\"" ++ NewRev ++ "\""}], {obj, [
+    send_json(Req, 201, [{"Etag", <<"\"", NewRev/binary, "\"">>}], {[
         {ok, true},
         {id, DocId},
         {rev, NewRev}
     ]});
 
 handle_doc_request(Req, 'COPY', _DbName, Db, SourceDocId) ->
-    SourceRev = case extract_header_rev(Req) of
-      missing_rev -> [];
-      Rev -> Rev
+    SourceRev =
+    case extract_header_rev(Req, proplists:get_value("rev", Req:parse_qs())) of
+        missing_rev -> [];
+        Rev -> Rev
     end,
 
     {TargetDocId, TargetRev} = parse_copy_destination_header(Req),
 
-    % open revision Rev or Current
+    % open revision Rev or Current  
     {Doc, _DocRev} = couch_doc_open(Db, SourceDocId, SourceRev, []),
 
     % save new doc
     {ok, NewTargetRev} = couch_db:update_doc(Db, Doc#doc{id=TargetDocId, revs=TargetRev}, []),
 
-    send_json(Req, 201, [{"Etag", "\"" ++ NewTargetRev ++ "\""}], {obj, [
+    send_json(Req, 201, [{"Etag", "\"" ++ binary_to_list(NewTargetRev) ++ "\""}], {[
         {ok, true},
         {id, TargetDocId},
         {rev, NewTargetRev}
     ]});
 
 handle_doc_request(Req, 'MOVE', _DbName, Db, SourceDocId) ->
-    SourceRev = case extract_header_rev(Req) of
-      missing_rev ->
+    SourceRev =
+    case extract_header_rev(Req, proplists:get_value("rev", Req:parse_qs())) of
+    missing_rev -> 
         throw({
-          bad_request,
-          "MOVE requires a specified rev parameter for the origin resource."}
-        );
-      Rev -> Rev
+            bad_request,
+            "MOVE requires a specified rev parameter for the origin resource."}
+            );
+    Rev -> Rev
     end,
 
     {TargetDocId, TargetRev} = parse_copy_destination_header(Req),
-
     % open revision Rev or Current
     {Doc, _DocRev} = couch_doc_open(Db, SourceDocId, SourceRev, []),
 
     % save new doc & delete old doc in one operation
     Docs = [
-      Doc#doc{id=TargetDocId, revs=TargetRev},
-      #doc{id=SourceDocId, revs=[SourceRev], deleted=true}
-    ],
+        Doc#doc{id=TargetDocId, revs=TargetRev},
+        #doc{id=SourceDocId, revs=[SourceRev], deleted=true}
+        ],
 
     {ok, ResultRevs} = couch_db:update_docs(Db, Docs, []),
 
     DocResults = lists:zipwith(
         fun(FDoc, NewRev) ->
-            {obj, [{"id", FDoc#doc.id}, {"rev", NewRev}]}
+            {[{id, FDoc#doc.id}, {rev, NewRev}]}
         end,
         Docs, ResultRevs),
-    send_json(Req, 201, {obj, [
+    send_json(Req, 201, {[
         {ok, true},
-        {new_revs, list_to_tuple(DocResults)}
+        {new_revs, DocResults}
     ]});
 
 handle_doc_request(_Req, _Method, _DbName, _Db, _DocId) ->
@@ -728,19 +705,19 @@
     case Rev of
     "" -> % open most recent rev
         case couch_db:open_doc(Db, DocId, Options) of
-            {ok, #doc{revs=[DocRev|_]}=Doc} ->
-                {Doc, DocRev};
-            Error ->
-                throw(Error)
-        end;
-    _ -> % open a specific rev (deletions come back as stubs)
-        case couch_db:open_doc_revs(Db, DocId, [Rev], Options) of
-            {ok, [{ok, Doc}]} ->
-                {Doc, Rev};
-            {ok, [Else]} ->
-                throw(Else)
-        end
-    end.
+        {ok, #doc{revs=[DocRev|_]}=Doc} ->
+            {Doc, DocRev};
+         Error ->
+             throw(Error)
+         end;
+  _ -> % open a specific rev (deletions come back as stubs)
+      case couch_db:open_doc_revs(Db, DocId, [Rev], Options) of
+          {ok, [{ok, Doc}]} ->
+              {Doc, Rev};
+          {ok, [Else]} ->
+              throw(Else)
+      end
+  end.
 
 % Attachment request handlers
 
@@ -773,8 +750,6 @@
 handle_attachment_request(Req, Method, _DbName, Db, DocId, FileName)
     when (Method == 'PUT') or (Method == 'DELETE') ->
 
-    Rev = extract_header_rev(Req),
-
     NewAttachment = case Method of
         'DELETE' ->
             [];
@@ -785,12 +760,12 @@
             }}]
     end,
 
-    Doc = case Rev of
+    Doc = case extract_header_rev(Req, proplists:get_value("rev", Req:parse_qs())) of
         missing_rev -> % make the new doc
             #doc{id=DocId};
-        _ ->
+        Rev ->
             case couch_db:open_doc_revs(Db, DocId, [Rev], []) of
-            {ok, [{ok, Doc0}]}   -> Doc0#doc{revs=[Rev]};
+            {ok, [{ok, Doc0}]}  -> Doc0#doc{revs=[Rev]};
             {ok, [Error]}       -> throw(Error)
             end
     end,
@@ -800,7 +775,7 @@
         attachments = NewAttachment ++ proplists:delete(FileName, Attachments)
     },
     {ok, UpdatedRev} = couch_db:update_doc(Db, DocEdited, []),
-    send_json(Req, case Method of 'DELETE' -> 200; _ -> 201 end, {obj, [
+    send_json(Req, case Method of 'DELETE' -> 200; _ -> 201 end, {[
         {ok, true},
         {id, DocId},
         {rev, UpdatedRev}
@@ -871,8 +846,8 @@
 
 % View request handling internals
 
-reverse_key_default(nil) -> <<>>;
-reverse_key_default(<<>>) -> nil;
+reverse_key_default(nil) -> {};
+reverse_key_default({}) -> nil;
 reverse_key_default(Key) -> Key.
 
 parse_view_query(Req) ->
@@ -882,16 +857,16 @@
         {"", _} ->
             Args;
         {"key", Value} ->
-            JsonKey = cjson:decode(Value),
+            JsonKey = ?JSON_DECODE(Value),
             Args#view_query_args{start_key=JsonKey,end_key=JsonKey};
         {"startkey_docid", DocId} ->
-            Args#view_query_args{start_docid=DocId};
+            Args#view_query_args{start_docid=list_to_binary(DocId)};
         {"endkey_docid", DocId} ->
-            Args#view_query_args{end_docid=DocId};
+            Args#view_query_args{end_docid=list_to_binary(DocId)};
         {"startkey", Value} ->
-            Args#view_query_args{start_key=cjson:decode(Value)};
+            Args#view_query_args{start_key=?JSON_DECODE(Value)};
         {"endkey", Value} ->
-            Args#view_query_args{end_key=cjson:decode(Value)};
+            Args#view_query_args{end_key=?JSON_DECODE(Value)};
         {"count", Value} ->
             case (catch list_to_integer(Value)) of
             Count when is_integer(Count) ->
@@ -966,11 +941,11 @@
     case Dir of
     fwd ->
         fun(ViewKey, ViewId) ->
-            couch_view:less_json({EndKey, EndDocId}, {ViewKey, ViewId})
+            couch_view:less_json([EndKey, EndDocId], [ViewKey, ViewId])
         end;
     rev->
         fun(ViewKey, ViewId) ->
-            couch_view:less_json({ViewKey, ViewId}, {EndKey, EndDocId})
+            couch_view:less_json([ViewKey, ViewId], [EndKey, EndDocId])
         end
     end,
 
@@ -991,18 +966,19 @@
                 lists:min([TotalViewCount - Offset, - AccCount]),
             JsonBegin = io_lib:format("{\"total_rows\":~w,\"offset\":~w,\"rows\":[\r\n",
                     [TotalViewCount, Offset2]),
-            Resp2:write_chunk(lists:flatten(JsonBegin)),
-            JsonObj = {obj, [{id, DocId}, {key, Key}, {value, Value}]},
-            {ok, {AccCount + 1, 0, Resp2, [cjson:encode(JsonObj) | AccRevRows]}};
+            Resp2:write_chunk(JsonBegin),
+            JsonObj = {[{id, DocId}, {key, Key}, {value, Value}]},
+            {ok, {AccCount + 1, 0, Resp2, [?JSON_ENCODE(JsonObj) | AccRevRows]}};
         {_, AccCount, _, Resp} ->
-            JsonObj = {obj, [{id, DocId}, {key, Key}, {value, Value}]},
-            {ok, {AccCount + 1, 0, Resp, [cjson:encode(JsonObj), ",\r\n" | AccRevRows]}}
+            
+            JsonObj = {[{id, DocId}, {key, Key}, {value, Value}]},
+            {ok, {AccCount + 1, 0, Resp, [?JSON_ENCODE(JsonObj), ",\r\n" | AccRevRows]}}
         end
     end,
 
     PosCountFun = fun({{Key, DocId}, Value}, OffsetReds,
                       {AccCount, AccSkip, Resp, AccRevRows}) ->
-        Offset = ReduceCountFun(OffsetReds),
+        Offset = ReduceCountFun(OffsetReds), % I think we only need this call once per view
         PassedEnd = PassedEndFun(Key, DocId),
         case {PassedEnd, AccCount, AccSkip, Resp} of
         {true, _, _, _} ->
@@ -1017,12 +993,13 @@
             Resp2 = start_json_response(Req, 200),
             JsonBegin = io_lib:format("{\"total_rows\":~w,\"offset\":~w,\"rows\":[\r\n",
                     [TotalViewCount, Offset]),
-            JsonObj = {obj, [{id, DocId}, {key, Key}, {value, Value}]},
-            Resp2:write_chunk(lists:flatten(JsonBegin ++ cjson:encode(JsonObj))),
+            JsonObj = {[{id, DocId}, {key, Key}, {value, Value}]},
+            
+            Resp2:write_chunk(JsonBegin ++ ?JSON_ENCODE(JsonObj)),
             {ok, {AccCount - 1, 0, Resp2, AccRevRows}};
         {_, AccCount, _, Resp} when (AccCount > 0) ->
-            JsonObj = {obj, [{"id", DocId}, {"key", Key}, {"value", Value}]},
-            Resp:write_chunk(",\r\n" ++  lists:flatten(cjson:encode(JsonObj))),
+            JsonObj = {[{id, DocId}, {key, Key}, {value, Value}]},
+            Resp:write_chunk(",\r\n" ++  ?JSON_ENCODE(JsonObj)),
             {ok, {AccCount - 1, 0, Resp, AccRevRows}}
         end
     end,
@@ -1036,13 +1013,13 @@
     {ok, {_, _, undefined, _}} ->
         % nothing found in the view, nothing has been returned
         % send empty view
-        send_json(Req, 200, {obj, [
+        send_json(Req, 200, {[
             {total_rows, TotalRows},
-            {rows, {}}
+            {rows, []}
         ]});
     {ok, {_, _, Resp, AccRevRows}} ->
         % end the view
-        Resp:write_chunk(lists:flatten(AccRevRows) ++ "\r\n]}"),
+        Resp:write_chunk(AccRevRows ++ "\r\n]}"),
         end_json_response(Resp);
     Error ->
         throw(Error)
@@ -1076,8 +1053,8 @@
         {"open_revs", "all"} ->
             Args#doc_query_args{open_revs=all};
         {"open_revs", RevsJsonStr} ->
-            JsonArray = cjson:decode(RevsJsonStr),
-            Args#doc_query_args{open_revs=tuple_to_list(JsonArray)};
+            JsonArray = ?JSON_DECODE(RevsJsonStr),
+            Args#doc_query_args{open_revs=JsonArray};
         _Else -> % unknown key value pair, ignore.
             Args
         end
@@ -1086,7 +1063,7 @@
 % Utilities
 
 none_match(Req, Tag) ->
-    Etag = "\"" ++ Tag ++ "\"",
+    Etag = "\"" ++ binary_to_list(Tag) ++ "\"",
     Etags = case Req:get_header_value("If-None-Match") of
         undefined ->
             [];
@@ -1109,9 +1086,9 @@
         _ ->
             lists:flatten(io_lib:format("~p", [Reason])) % else term to text
         end,
-    Json = {obj, [
-        {error, atom_to_list(Atom)},
-        {reason, FormattedReason}
+    Json = {[
+        {error, Atom},
+        {reason, list_to_binary(FormattedReason)}
     ]},
     {HttpCode, Json}.
 
@@ -1136,17 +1113,18 @@
 error_to_json0(Error) ->
     {500, error, Error}.
 
-extract_header_rev(Req) ->
-    QueryRev = proplists:get_value("rev", Req:parse_qs()),
+extract_header_rev(Req, ExplictRev) when is_list(ExplictRev)->
+    extract_header_rev(Req, list_to_binary(ExplictRev));
+extract_header_rev(Req, ExplictRev) ->
     Etag = case Req:get_header_value("If-Match") of
         undefined -> undefined;
         Tag -> string:strip(Tag, both, $")
     end,
-    case {QueryRev, Etag} of
+    case {ExplictRev, Etag} of
     {undefined, undefined} -> missing_rev;
-    {_, undefined} -> QueryRev;
-    {undefined, _} -> Etag;
-    _ when QueryRev == Etag -> Etag;
+    {_, undefined} -> ExplictRev;
+    {undefined, _} -> list_to_binary(Etag);
+    _ when ExplictRev == Etag -> list_to_binary(Etag);
     _ ->
         throw({bad_request, "Document rev and etag have different values"})
     end.
@@ -1154,12 +1132,12 @@
 parse_copy_destination_header(Req) ->
     Destination = Req:get_header_value("Destination"),
     case regexp:match(Destination, "\\?") of
-        nomatch ->
-            {Destination, []};
-        {match, _, _} ->
-            {ok, [DocId, RevQueryOptions]} = regexp:split(Destination, "\\?"),
-            {ok, [_RevQueryKey, Rev]} = regexp:split(RevQueryOptions, "="),
-            {DocId, [Rev]}
+    nomatch -> 
+        {list_to_binary(Destination), []};
+    {match, _, _} ->
+        {ok, [DocId, RevQueryOptions]} = regexp:split(Destination, "\\?"),
+        {ok, [_RevQueryKey, Rev]} = regexp:split(RevQueryOptions, "="),
+        {list_to_binary(DocId), [list_to_binary(Rev)]}
     end.
 
 send_error(Req, {method_not_allowed, Methods}) ->
@@ -1187,7 +1165,7 @@
         {"Content-Type", negotiate_content_type(Req)},
         {"Cache-Control", "must-revalidate"}
     ] ++ server_header(),
-    Body = cjson:encode(Value),
+    Body = ?JSON_ENCODE(Value),
     Resp = Req:respond({Code, DefaultHeaders ++ Headers, Body}),
     {ok, Resp}.
 

Modified: incubator/couchdb/trunk/src/couchdb/couch_query_servers.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_query_servers.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_query_servers.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_query_servers.erl Sun Aug 31 02:43:41 2008
@@ -18,7 +18,7 @@
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3,stop/0]).
 -export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
 -export([reduce/3, rereduce/3]).
--export([test/0]).
+% -export([test/0]).
 
 -include("couch_db.hrl").
 
@@ -32,7 +32,6 @@
     readline(Port, []).
 
 readline(Port, Acc) ->
-    
     case get(query_server_timeout) of
     undefined ->
         Timeout = list_to_integer(couch_config:get(
@@ -44,7 +43,7 @@
     {Port, {data, {noeol, Data}}} ->
         readline(Port, [Data|Acc]);
     {Port, {data, {eol, Data}}} ->
-        lists:flatten(lists:reverse(Acc, Data));
+        lists:reverse(Acc, Data);
     {Port, Err} ->
         catch port_close(Port),
         throw({map_process_error, Err})
@@ -54,8 +53,9 @@
     end.
 
 read_json(Port) ->
-    case cjson:decode(readline(Port)) of
-    {obj, [{"log", Msg}]} when is_list(Msg) ->
+    Line = readline(Port),
+    case ?JSON_DECODE(Line) of
+    {[{<<"log">>,Msg}]} when is_binary(Msg) ->
         % we got a message to log. Log it and continue
         ?LOG_INFO("Query Server Log Message: ~s", [Msg]),
         read_json(Port);
@@ -63,17 +63,15 @@
         Else
     end.
 
-writeline(Port, String) ->
-    true = port_command(Port, String ++ "\n").
-
 % send command and get a response.
 prompt(Port, Json) ->
-    writeline(Port, cjson:encode(Json)),
+    Bin = iolist_to_binary([?JSON_ENCODE(Json) , "\n"]),
+    true = port_command(Port, Bin),
     case read_json(Port) of
-    {obj, [{"error", Id}, {"reason", Reason}]} ->
-        throw({list_to_atom(Id),Reason});
-    {obj, [{"reason", Reason}, {"error", Id}]} ->
-        throw({list_to_atom(Id),Reason});
+    {[{<<"error">>, Id}, {<<"reason">>, Reason}]} ->
+        throw({list_to_atom(binary_to_list(Id)),Reason});
+    {[{<<"reason">>, Reason}, {<<"error">>, Id}]} ->
+        throw({list_to_atom(binary_to_list(Id)),Reason});
     Result ->
         Result
     end.
@@ -83,7 +81,7 @@
     Port = get_linked_port(Lang),
     % send the functions as json strings
     lists:foreach(fun(FunctionSource) ->
-            true = prompt(Port, {"add_fun", FunctionSource})
+            true = prompt(Port, [<<"add_fun">>, FunctionSource])
         end,
         Functions),
     {ok, {Lang, Port}}.
@@ -93,13 +91,19 @@
     Results = lists:map(
         fun(Doc) ->
             Json = couch_doc:to_json_obj(Doc, []),
-            Results = prompt(Port, {"map_doc", Json}),
+            
+            FunsResults = prompt(Port, [<<"map_doc">>, Json]),
             % the results are a json array of function map yields like this:
-            % {FunResults1, FunResults2 ...}
+            % [FunResults1, FunResults2 ...]
             % where funresults is are json arrays of key value pairs:
-            % {{Key1, Value1}, {Key2, Value2}}
-            % Convert to real lists, execept the key, value pairs
-            [tuple_to_list(FunResult) || FunResult <- tuple_to_list(Results)]
+            % [[Key1, Value1], [Key2, Value2]]
+            % Convert the key, value pairs to tuples like
+            % [{Key1, Value1}, {Key2, Value2}]
+            lists:map(
+                fun(FunRs) ->
+                    [list_to_tuple(FunResult) || FunResult <- FunRs]
+                end,
+            FunsResults)
         end,
         Docs),
     {ok, Results}.
@@ -114,12 +118,13 @@
     case gen_server:call(couch_query_servers, {get_port, Lang}) of
     {ok, Port0} ->
         link(Port0),
-        true = prompt(Port0, {"reset"}),
+        true = prompt(Port0, [<<"reset">>]),
         Port0;
     {empty, Cmd} ->
         ?LOG_INFO("Spawning new ~s instance.", [Lang]),
         open_port({spawn, Cmd}, [stream,
                                     {line, 1000},
+                                    binary,
                                     exit_status,
                                     hide]);
     Error ->
@@ -152,8 +157,8 @@
     Grouped = group_reductions_results(ReducedValues),
     Results = lists:zipwith(
         fun(FunSrc, Values) ->
-            {true, {Result}} = 
-                prompt(Port, {"rereduce", {FunSrc}, list_to_tuple(Values)}),
+            [true, [Result]] = 
+                prompt(Port, [<<"rereduce">>, [FunSrc], Values]),
             Result
         end, RedSrcs, Grouped),
         
@@ -164,10 +169,10 @@
     {ok, []};
 reduce(Lang, RedSrcs, KVs) ->
     Port = get_linked_port(Lang),
-    {true, Results} = prompt(Port, 
-            {"reduce", list_to_tuple(RedSrcs), list_to_tuple(KVs)}),
+    [true, Results] = prompt(Port, 
+            [<<"reduce">>, RedSrcs, KVs]),
     return_linked_port(Lang, Port),
-    {ok, tuple_to_list(Results)}.
+    {ok, Results}.
 
 
 init([]) ->
@@ -182,18 +187,27 @@
             ?MODULE:stop()
         end),
         
-    QueryServerList = couch_config:lookup_match(
+    QueryServers = couch_config:lookup_match(
             {{"query_servers", '$1'}, '$2'}, []),
+    QueryServers2 = 
+        [{list_to_binary(Lang), Path} || {Lang, Path} <- QueryServers],
         
-    {ok, {QueryServerList, []}}.
+    {ok, {QueryServers2, []}}.
 
 terminate(_Reason, _Server) ->
     ok.
 
 
 handle_call({get_port, Lang}, {FromPid, _}, {QueryServerList, LangPorts}) ->
-    case lists:keysearch(Lang, 1, LangPorts) of
-    {value, {_, Port}=LangPort} ->
+    case proplists:get_value(Lang, LangPorts) of
+    undefined ->
+        case proplists:get_value(Lang, QueryServerList) of
+        undefined -> % not a supported language
+            {reply, {query_language_unknown, Lang}, {QueryServerList, LangPorts}};
+        ServerCmd ->
+            {reply, {empty, ServerCmd}, {QueryServerList, LangPorts}}
+        end;
+    Port ->
         Result =
         case catch port_connect(Port, FromPid) of
         true ->
@@ -203,14 +217,7 @@
             catch port_close(Port),
             Error
         end,
-        {reply, Result, {QueryServerList, LangPorts -- [LangPort]}};
-    false ->
-        case lists:keysearch(Lang, 1, QueryServerList) of
-        {value, {_, ServerCmd}} ->
-            {reply, {empty, ServerCmd}, {QueryServerList, LangPorts}};
-        false -> % not a supported language
-            {reply, {query_language_unknown, Lang}, {QueryServerList, LangPorts}}
-        end
+        {reply, Result, {QueryServerList, LangPorts -- [{Lang,Port}]}}
     end;
 handle_call({return_port, {Lang, Port}}, _From, {QueryServerList, LangPorts}) ->
     case catch port_connect(Port, self()) of
@@ -241,11 +248,11 @@
 
 % test() ->
 %     test("../js/js -f main.js").
-
-test() ->
-    start_link(),
-    {ok, DocMap} = start_doc_map("javascript", ["function(doc) {if (doc[0] == 'a') return doc[1];}"]),
-    {ok, Results} = map_docs(DocMap, [#doc{body={"a", "b"}}, #doc{body={"c", "d"}},#doc{body={"a", "c"}}]),
-    io:format("Results: ~w~n", [Results]),
-    stop_doc_map(DocMap),
-    ok.
+% 
+% test(Cmd) ->
+%     start_link(Cmd),
+%     {ok, DocMap} = start_doc_map(<<"javascript">>, [<<"function(doc) {if (doc[0] == 'a') return doc[1];}">>]),
+%     {ok, Results} = map_docs(DocMap, [#doc{body={"a", "b"}}, #doc{body={"c", "d"}},#doc{body={"a", "c"}}]),
+%     io:format("Results: ~w~n", [Results]),
+%     stop_doc_map(DocMap),
+%     ok.

Modified: incubator/couchdb/trunk/src/couchdb/couch_rep.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_rep.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_rep.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_rep.erl Sun Aug 31 02:43:41 2008
@@ -16,6 +16,8 @@
 
 -export([replicate/2, replicate/3]).
 
+url_encode(Bin) when is_binary(Bin) ->
+    url_encode(binary_to_list(Bin));
 url_encode([H|T]) ->
     if
     H >= $a, $z >= H ->
@@ -56,8 +58,10 @@
     
 replicate2(Source, DbSrc, Target, DbTgt, Options) ->
     {ok, HostName} = inet:gethostname(),
-
-    RepRecKey = ?LOCAL_DOC_PREFIX ++ HostName ++ ":" ++ Source ++ ":" ++ Target,
+    HostNameBin = list_to_binary(HostName),
+    RepRecKey = <<?LOCAL_DOC_PREFIX, HostNameBin/binary, 
+            ":", Source/binary, ":", Target/binary>>,
+    
     StartTime = httpd_util:rfc1123_date(),
     
     case proplists:get_value(full, Options, false)
@@ -82,14 +86,14 @@
         end
     end,
 
-    #doc{body={obj,OldRepHistoryProps}} = RepRecSrc,
-    #doc{body={obj,OldRepHistoryPropsTrg}} = RepRecTgt,
+    #doc{body={OldRepHistoryProps}} = RepRecSrc,
+    #doc{body={OldRepHistoryPropsTrg}} = RepRecTgt,
 
     SeqNum =
     case OldRepHistoryProps == OldRepHistoryPropsTrg of
     true ->
         % if the records are identical, then we have a valid replication history
-        proplists:get_value("source_last_seq", OldRepHistoryProps, 0);
+        proplists:get_value(<<"source_last_seq">>, OldRepHistoryProps, 0);
     false ->
         ?LOG_INFO("Replication records differ. "
                 "Performing full replication instead of incremental.", []),
@@ -97,26 +101,26 @@
         0
     end,
 
-    {NewSeqNum, Stats} = pull_rep(DbTgt, DbSrc, SeqNum),
+    {NewSeqNum, Stats} = pull_rep(DbTgt, DbSrc, SeqNum),    
     
     case NewSeqNum == SeqNum andalso OldRepHistoryProps /= [] of
     true ->
         % nothing changed, don't record results
-        {ok, {obj, OldRepHistoryProps}};
+        {ok, {OldRepHistoryProps}};
     false ->
         HistEntries =[
-            {obj,
-                [{"start_time", StartTime},
-                {"end_time", httpd_util:rfc1123_date()},
-                {"start_last_seq", SeqNum},
-                {"end_last_seq", NewSeqNum} | Stats]}
-            | tuple_to_list(proplists:get_value("history", OldRepHistoryProps, {}))],
+            {
+                [{start_time, list_to_binary(StartTime)},
+                {end_time, list_to_binary(httpd_util:rfc1123_date())},
+                {start_last_seq, SeqNum},
+                {end_last_seq, NewSeqNum} | Stats]}
+            | proplists:get_value("history", OldRepHistoryProps, [])],
         % something changed, record results
         NewRepHistory =
-            {obj,
-                [{"session_id", couch_util:new_uuid()},
-                {"source_last_seq", NewSeqNum},
-                {"history", list_to_tuple(lists:sublist(HistEntries, 50))}]},
+            {
+                [{session_id, couch_util:new_uuid()},
+                {source_last_seq, NewSeqNum},
+                {history, lists:sublist(HistEntries, 50)}]},
 
         {ok, _} = update_doc(DbSrc, RepRecSrc#doc{body=NewRepHistory}, []),
         {ok, _} = update_doc(DbTgt, RepRecTgt#doc{body=NewRepHistory}, []),
@@ -165,6 +169,7 @@
     receive
     {Src, Id, Revs} ->
         Src ! got_it,
+
         MissingRevs =
         case get_missing_revs(DbTarget, [{Id, Revs}]) of
         {ok, [{Id, MissingRevs0}]} ->
@@ -179,8 +184,8 @@
                 RevsChecked + length(Revs),
                 MissingFound + length(MissingRevs));
     {Src, shutdown} ->
-        Src ! {done, self(), [{"missing_checked", RevsChecked},
-                                 {"missing_found", MissingFound}]}
+        Src ! {done, self(), [{missing_checked, RevsChecked},
+                                 {missing_found, MissingFound}]}
     end.
     
 
@@ -195,7 +200,7 @@
         SaveDocsPid ! {self(), docs, Docs},
         open_doc_revs_loop(DbSource, SaveDocsPid, DocsRead + length(Docs));
     {Src, shutdown} ->
-        Src ! {done, self(), [{"docs_read", DocsRead}]}
+        Src ! {done, self(), [{docs_read, DocsRead}]}
     end.
 
 
@@ -207,7 +212,7 @@
         ok = save_docs(DbTarget, Docs, []),
         save_docs_loop(DbTarget, DocsWritten + length(Docs));
     {Src, shutdown} ->
-        Src ! {done, self(), [{"docs_written", DocsWritten}]}
+        Src ! {done, self(), [{docs_written, DocsWritten}]}
     end.
 
 
@@ -224,12 +229,12 @@
     [] ->
         {Url, []};
     _ ->
-        {Url, [], "application/json; charset=utf-8", lists:flatten(cjson:encode(JsonBody))}
+        {Url, [], "application/json; charset=utf-8", iolist_to_binary(?JSON_ENCODE(JsonBody))}
     end,
     {ok, {{_, ResponseCode,_},_Headers, ResponseBody}} = http:request(Action, Request, [], []),
     if
     ResponseCode >= 200, ResponseCode < 500 ->
-        cjson:decode(ResponseBody)
+        ?JSON_DECODE(ResponseBody)
     end.
 
 enum_docs0(_InFun, [], Acc) ->
@@ -240,17 +245,25 @@
     {stop, Acc2} -> Acc2
     end.
 
-open_db("http" ++ DbName)->
-    case lists:last(DbName) of
+fix_url(UrlBin) ->
+    Url = binary_to_list(UrlBin),
+    case lists:last(Url) of
     $/ ->
-        {ok, "http" ++ DbName};
+        {ok, Url};
     _ ->
-        {ok, "http" ++ DbName ++ "/"}
-    end;
+        {ok, Url ++ "/"}
+    end.
+
+open_db(<<"http://", _/binary>>=UrlBin)->
+    fix_url(UrlBin);
+open_db(<<"https://", _/binary>>=UrlBin)->
+    fix_url(UrlBin);
 open_db(DbName)->
     couch_db:open(DbName, []).
 
-close_db("http" ++ _)->
+close_db("http://" ++ _)->
+    ok;
+close_db("https://" ++ _)->
     ok;
 close_db(DbName)->
     couch_db:close(DbName).
@@ -258,20 +271,20 @@
 
 enum_docs_since(DbUrl, StartSeq, InFun, InAcc) when is_list(DbUrl) ->
     Url = DbUrl ++ "_all_docs_by_seq?count=100&startkey=" ++ integer_to_list(StartSeq),
-    {obj, Results} = do_http_request(Url, get),
+    {Results} = do_http_request(Url, get),
     DocInfoList=
-    lists:map(fun({obj, RowInfoList}) ->
-        {obj, RowValueProps} = proplists:get_value("value", RowInfoList),
+    lists:map(fun({RowInfoList}) ->
+        {RowValueProps} = proplists:get_value(<<"value">>, RowInfoList),
         #doc_info{
-            id=proplists:get_value("id", RowInfoList),
-            rev=proplists:get_value("rev", RowValueProps),
-            update_seq = proplists:get_value("key", RowInfoList),
+            id=proplists:get_value(<<"id">>, RowInfoList),
+            rev=proplists:get_value(<<"rev">>, RowValueProps),
+            update_seq = proplists:get_value(<<"key">>, RowInfoList),
             conflict_revs =
-                tuple_to_list(proplists:get_value("conflicts", RowValueProps, {})),
+                proplists:get_value(<<"conflicts">>, RowValueProps, []),
             deleted_conflict_revs =
-                tuple_to_list(proplists:get_value("deleted_conflicts", RowValueProps, {})),
-            deleted = proplists:get_value("deleted", RowValueProps, false)}
-        end, tuple_to_list(proplists:get_value("rows", Results))),
+                proplists:get_value(<<"deleted_conflicts">>, RowValueProps, []),
+            deleted = proplists:get_value(<<"deleted">>, RowValueProps, false)}
+        end, proplists:get_value(<<"rows">>, Results)),
     case DocInfoList of
     [] ->
         {ok, InAcc};
@@ -284,22 +297,18 @@
     couch_db:enum_docs_since(DbSource, StartSeq, Fun, Acc).
 
 get_missing_revs(DbUrl, DocIdRevsList) when is_list(DbUrl) ->
-    JsonDocIdRevsList = {obj,
-        [{Id, list_to_tuple(RevList)} || {Id, RevList} <- DocIdRevsList]},
-    {obj, ResponseMembers} =
-    do_http_request(DbUrl ++ "_missing_revs",
-        post, JsonDocIdRevsList),
-    {obj, DocMissingRevsList} = proplists:get_value("missing_revs", ResponseMembers),
-    {ok, [{Id, tuple_to_list(MissingRevs)} || {Id, MissingRevs} <- DocMissingRevsList]};
+    {ResponseMembers} = do_http_request(DbUrl ++ "_missing_revs", post, {DocIdRevsList}),
+    {DocMissingRevsList} = proplists:get_value(<<"missing_revs">>, ResponseMembers),
+    {ok, DocMissingRevsList};
 get_missing_revs(Db, DocId) ->
     couch_db:get_missing_revs(Db, DocId).
 
 
 update_doc(DbUrl, #doc{id=DocId}=Doc, _Options) when is_list(DbUrl) ->
     Url = DbUrl ++ url_encode(DocId),
-    {obj, ResponseMembers} =
+    {ResponseMembers} =
         do_http_request(Url, put, couch_doc:to_json_obj(Doc, [revs,attachments])),
-    RevId = proplists:get_value("_rev", ResponseMembers),
+    RevId = proplists:get_value(<<"_rev">>, ResponseMembers),
     {ok, RevId};
 update_doc(Db, Doc, Options) ->
     couch_db:update_doc(Db, Doc, Options).
@@ -308,9 +317,9 @@
     ok;
 save_docs(DbUrl, Docs, []) when is_list(DbUrl) ->
     JsonDocs = [couch_doc:to_json_obj(Doc, [revs,attachments]) || Doc <- Docs],
-    {obj, Returned} =
-        do_http_request(DbUrl ++ "_bulk_docs", post, {obj, [{new_edits, false}, {docs, list_to_tuple(JsonDocs)}]}),
-    true = proplists:get_value("ok", Returned),
+    {Returned} =
+        do_http_request(DbUrl ++ "_bulk_docs", post, {[{new_edits, false}, {docs, JsonDocs}]}),
+    true = proplists:get_value(<<"ok">>, Returned),
     ok;
 save_docs(Db, Docs, Options) ->
     couch_db:save_docs(Db, Docs, Options).
@@ -318,8 +327,8 @@
 
 open_doc(DbUrl, DocId, []) when is_list(DbUrl) ->
     case do_http_request(DbUrl ++ url_encode(DocId), get) of
-    {obj, [{"error", ErrId}, {"reason", Reason}]} ->
-        {list_to_atom(ErrId), Reason};
+    {[{<<"error">>, ErrId}, {<<"reason">>, Reason}]} -> % binaries?
+        {list_to_atom(binary_to_list(ErrId)), Reason};
     Doc  ->
         {ok, couch_doc:from_json_obj(Doc)}
     end;
@@ -333,16 +342,16 @@
             % latest is only option right now
             "latest=true"
         end, Options),
-    RevsQueryStrs = lists:flatten(cjson:encode(list_to_tuple(Revs))),
-    Url = DbUrl ++ DocId ++ "?" ++ couch_util:implode(["revs=true", "attachments=true", "open_revs=" ++ RevsQueryStrs ] ++ QueryOptionStrs, "&"),
+    RevsQueryStrs = lists:flatten(?JSON_ENCODE(Revs)),
+    Url = DbUrl ++ binary_to_list(DocId) ++ "?" ++ couch_util:implode(["revs=true", "attachments=true", "open_revs=" ++ RevsQueryStrs ] ++ QueryOptionStrs, "&"),
     JsonResults = do_http_request(Url, get, []),
     Results =
     lists:map(
-        fun({obj, [{"missing", Rev}]}) ->
+        fun({[{<<"missing">>, Rev}]}) ->
             {{not_found, missing}, Rev};
-        ({obj, [{"ok", JsonDoc}]}) ->
+        ({[{<<"ok">>, JsonDoc}]}) ->
             {ok, couch_doc:from_json_obj(JsonDoc)}
-        end, tuple_to_list(JsonResults)),
+        end, JsonResults),
     {ok, Results};
 open_doc_revs(Db, DocId, Revs, Options) ->
     couch_db:open_doc_revs(Db, DocId, Revs, Options).

Modified: incubator/couchdb/trunk/src/couchdb/couch_server.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_server.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_server.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_server.erl Sun Aug 31 02:43:41 2008
@@ -124,7 +124,7 @@
             [$/ | RelativeFilename] -> ok;
             RelativeFilename -> ok
             end,
-            [filename:rootname(RelativeFilename, ".couch") | AccIn]
+            [list_to_binary(filename:rootname(RelativeFilename, ".couch")) | AccIn]
         end, []),
     {ok, Filenames}.
 
@@ -174,9 +174,10 @@
 handle_call(get_root, _From, #server{root_dir=Root}=Server) ->
     {reply, {ok, Root}, Server};
 handle_call({open, DbName, Options}, {FromPid,_}, Server) ->
-    case check_dbname(Server, DbName) of
+    DbNameList = binary_to_list(DbName),
+    case check_dbname(Server, DbNameList) of
     ok ->
-        Filepath = get_full_filename(Server, DbName),
+        Filepath = get_full_filename(Server, DbNameList),
         LruTime = now(),
         case ets:lookup(couch_dbs_by_name, DbName) of
         [] ->    
@@ -207,9 +208,10 @@
         {reply, Error, Server}
     end;
 handle_call({create, DbName, Options}, {FromPid,_}, Server) ->
-    case check_dbname(Server, DbName) of
+    DbNameList = binary_to_list(DbName),
+    case check_dbname(Server, DbNameList) of
     ok ->
-        Filepath = get_full_filename(Server, DbName),
+        Filepath = get_full_filename(Server, DbNameList),
 
         case ets:lookup(couch_dbs_by_name, DbName) of
         [] ->
@@ -233,9 +235,10 @@
         {reply, Error, Server}
     end;
 handle_call({delete, DbName}, _From, Server) ->
-    case check_dbname(Server, DbName) of
+    DbNameList = binary_to_list(DbName),
+    case check_dbname(Server, DbNameList) of
     ok ->
-        FullFilepath = get_full_filename(Server, DbName),
+        FullFilepath = get_full_filename(Server, DbNameList),
         Server2 = 
         case ets:lookup(couch_dbs_by_name, DbName) of
         [] -> Server;

Modified: incubator/couchdb/trunk/src/couchdb/couch_util.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_util.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_util.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_util.erl Sun Aug 31 02:43:41 2008
@@ -18,6 +18,7 @@
 -export([abs_pathname/1,abs_pathname/2, trim/1, ascii_lower/1]).
 -export([encodeBase64/1, decodeBase64/1, to_hex/1]).
 
+-include("couch_db.hrl").
 
 % arbitrarily chosen amount of memory to use before flushing to disk
 -define(FLUSH_MAX_MEM, 10000000).
@@ -33,7 +34,7 @@
     end.
 
 new_uuid() ->
-    to_hex(crypto:rand_bytes(16)).
+    list_to_binary(to_hex(crypto:rand_bytes(16))).
     
 to_hex([]) ->
     [];
@@ -141,27 +142,30 @@
         put(couch_drv_port, Port),
         Port;
     Port ->
-     Port
+        Port
     end.
 
 collate(A, B) ->
     collate(A, B, []).
 
-collate(A, B, Options) when is_list(A), is_list(B) ->
+collate(A, B, Options) when is_binary(A), is_binary(B) ->
     Operation =
     case lists:member(nocase, Options) of
         true -> 1; % Case insensitive
         false -> 0 % Case sensitive
     end,
-    Port = drv_port(),
-    LenA = length(A),
-    LenB = length(B),
-    Bin = list_to_binary([<<LenA:32/native>>, A, <<LenB:32/native>>, B]),
-    case erlang:port_control(Port, Operation, Bin) of
-        [0] -> -1;
-        [1] -> 1;
-        [2] -> 0
-    end.
+
+    SizeA = size(A),
+    SizeB = size(B),
+    Bin = <<SizeA:32/native, A/binary, SizeB:32/native, B/binary>>,
+    [Result] = erlang:port_control(drv_port(), Operation, Bin),
+    % Result is 0 for lt, 1 for eq and 2 for gt. Subtract 1 to return the
+    % expected typical -1, 0, 1
+    Result - 1;
+
+collate(A, B, _Options) ->
+    io:format("-----A,B:~p,~p~n", [A,B]),
+    throw({error, badtypes}).
 
 should_flush() ->
     should_flush(?FLUSH_MAX_MEM).
@@ -187,7 +191,6 @@
 %%% erlang ssl library
 
 -define(st(X,A), ((X-A+256) div 256)).
--define(CHARS, 64).
 
 %% A PEM encoding consists of characters A-Z, a-z, 0-9, +, / and
 %% =. Each character encodes a 6 bits value from 0 to 63 (A = 0, / =
@@ -195,42 +198,47 @@
 %%
 
 %%
-%% encode64(Bytes|Binary) -> Chars
+%% encode64(Bytes|Binary) -> binary
 %%
 %% Take 3 bytes a time (3 x 8 = 24 bits), and make 4 characters out of
 %% them (4 x 6 = 24 bits).
 %%
 encodeBase64(Bs) when list(Bs) ->
-    encodeBase64(list_to_binary(Bs));
-encodeBase64(<<B:3/binary, Bs/binary>>) ->
+    encodeBase64(list_to_binary(Bs), <<>>);
+encodeBase64(Bs) ->
+    encodeBase64(Bs, <<>>).
+    
+encodeBase64(<<B:3/binary, Bs/binary>>, Acc) ->
     <<C1:6, C2:6, C3:6, C4:6>> = B,
-    [enc(C1), enc(C2), enc(C3), enc(C4)| encodeBase64(Bs)];
-encodeBase64(<<B:2/binary>>) ->
+    encodeBase64(Bs, <<Acc/binary, (enc(C1)), (enc(C2)), (enc(C3)), (enc(C4))>>);
+encodeBase64(<<B:2/binary>>, Acc) ->
     <<C1:6, C2:6, C3:6, _:6>> = <<B/binary, 0>>,
-    [enc(C1), enc(C2), enc(C3), $=];
-encodeBase64(<<B:1/binary>>) ->
+    <<Acc/binary, (enc(C1)), (enc(C2)), (enc(C3)), $=>>;
+encodeBase64(<<B:1/binary>>, Acc) ->
     <<C1:6, C2:6, _:12>> = <<B/binary, 0, 0>>,
-    [enc(C1), enc(C2), $=, $=];
-encodeBase64(<<>>) ->
-    [].
+    <<Acc/binary, (enc(C1)), (enc(C2)), $=, $=>>;
+encodeBase64(<<>>, Acc) ->
+    Acc.
 
 %%
-%% decodeBase64(Chars) -> Binary
+%% decodeBase64(BinaryChars) -> Binary
 %%
+decodeBase64(Cs) when is_list(Cs)->
+    decodeBase64(list_to_binary(Cs));
 decodeBase64(Cs) ->
-    list_to_binary(decode1(Cs)).
+    decode1(Cs, <<>>).
 
-decode1([C1, C2, $=, $=]) ->
+decode1(<<C1, C2, $=, $=>>, Acc) ->
     <<B1, _:16>> = <<(dec(C1)):6, (dec(C2)):6, 0:12>>,
-    [B1];
-decode1([C1, C2, C3, $=]) ->
+    <<Acc/binary, B1>>;
+decode1(<<C1, C2, C3, $=>>, Acc) ->
     <<B1, B2, _:8>> = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(0)):6>>,
-    [B1, B2];
-decode1([C1, C2, C3, C4| Cs]) ->
-    Bin = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(C4)):6>>,
-    [Bin| decode1(Cs)];
-decode1([]) ->
-    [].
+    <<Acc/binary, B1, B2>>;
+decode1(<<C1, C2, C3, C4, Cs/binary>>, Acc) ->
+    Bin = <<Acc/binary, (dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(C4)):6>>,
+    decode1(Cs, Bin);
+decode1(<<>>, Acc) ->
+    Acc.
 
 %% enc/1 and dec/1
 %%

Modified: incubator/couchdb/trunk/src/couchdb/couch_view.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_view.erl?rev=690668&r1=690667&r2=690668&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_view.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_view.erl Sun Aug 31 02:43:41 2008
@@ -94,6 +94,13 @@
         N -> {ok, {reduce, N, Lang, View}}
     end.
 
+detuple_kvs([], Acc) ->
+    lists:reverse(Acc);
+detuple_kvs([KV | Rest], Acc) ->
+    {{Key,Id},Value} = KV,
+    NKV = [[Key, Id], Value],
+    detuple_kvs(Rest, [NKV | Acc]).
+
 expand_dups([], Acc) ->
     lists:reverse(Acc);
 expand_dups([{Key, {dups, Vals}} | Rest], Acc) ->
@@ -111,13 +118,13 @@
     couch_btree:fold_reduce(Bt, Dir, StartKey, EndKey, GroupFun,
             WrapperFun, Acc);
 
-fold_reduce({reduce, NthRed, Lang, #view{btree=Bt, reduce_funs=RedFuns}}, Dir, StartKey, EndKey, GroupFun, Fun, Acc) ->
+fold_reduce({reduce, NthRed, Lang, #view{btree=Bt, reduce_funs=RedFuns}}, Dir, StartKey, EndKey, GroupFun, Fun, Acc) ->    
     PreResultPadding = lists:duplicate(NthRed - 1, []),
     PostResultPadding = lists:duplicate(length(RedFuns) - NthRed, []),
     {_Name, FunSrc} = lists:nth(NthRed,RedFuns),
     ReduceFun =
         fun(reduce, KVs) ->
-            {ok, Reduced} = couch_query_servers:reduce(Lang, [FunSrc], expand_dups(KVs, [])),
+            {ok, Reduced} = couch_query_servers:reduce(Lang, [FunSrc], detuple_kvs(expand_dups(KVs, []),[])),
             {0, PreResultPadding ++ Reduced ++ PostResultPadding};
         (rereduce, Reds) ->
             UserReds = [[lists:nth(NthRed, UserRedsList)] || {_, UserRedsList} <- Reds],
@@ -167,16 +174,16 @@
     Count.
                 
 
-design_doc_to_view_group(#doc{id=Id,body={obj, Fields}}) ->
-    Language = proplists:get_value("language", Fields, "javascript"),
-    {obj, RawViews} = proplists:get_value("views", Fields, {obj, []}),
+design_doc_to_view_group(#doc{id=Id,body={Fields}}) ->
+    Language = proplists:get_value(<<"language">>, Fields, <<"javascript">>),
+    {RawViews} = proplists:get_value(<<"views">>, Fields, {[]}),
             
     % add the views to a dictionary object, with the map source as the key
     DictBySrc =
     lists:foldl(
-        fun({Name, {obj, MRFuns}}, DictBySrcAcc) ->
-            MapSrc = proplists:get_value("map", MRFuns),
-            RedSrc = proplists:get_value("reduce", MRFuns, null),
+        fun({Name, {MRFuns}}, DictBySrcAcc) ->
+            MapSrc = proplists:get_value(<<"map">>, MRFuns),
+            RedSrc = proplists:get_value(<<"reduce">>, MRFuns, null),
             View =
             case dict:find(MapSrc, DictBySrcAcc) of
                 {ok, View0} -> View0;
@@ -248,15 +255,15 @@
     ok.
 
 
-handle_call({start_temp_updater, DbName, Lang, MapSrc, RedSrc}, _From, #server{root_dir=Root}=Server) ->
-    <<SigInt:128/integer>> = erlang:md5(Lang ++ [0] ++ MapSrc ++ [0] ++ RedSrc),
+handle_call({start_temp_updater, DbName, Lang, MapSrc, RedSrc}, _From, #server{root_dir=Root}=Server) ->    
+    <<SigInt:128/integer>> = erlang:md5(term_to_binary({Lang, MapSrc, RedSrc})),
     Name = lists:flatten(io_lib:format("_temp_~.36B",[SigInt])),
     Pid = 
     case ets:lookup(couch_views_by_name, {DbName, Name}) of
     [] ->
         case ets:lookup(couch_views_temp_fd_by_db, DbName) of
         [] ->
-            FileName = Root ++ "/." ++ DbName ++ "_temp",
+            FileName = Root ++ "/." ++ binary_to_list(DbName) ++ "_temp",
             {ok, Fd} = couch_file:open(FileName, [create, overwrite]),
             Count = 0;
         [{_, Fd, Count}] ->
@@ -298,7 +305,7 @@
             end
         end, Names),
     delete_index_dir(Root, DbName),
-    file:delete(Root ++ "/." ++ DbName ++ "_temp"),
+    file:delete(Root ++ "/." ++ binary_to_list(DbName) ++ "_temp"),
     {noreply, Server}.
 
 handle_info({'EXIT', _FromPid, normal}, Server) ->
@@ -314,7 +321,7 @@
         case Count of
         1 -> % Last ref
             couch_file:close(Fd),
-            file:delete(RootDir ++ "/." ++ DbName ++ "_temp"),
+            file:delete(RootDir ++ "/." ++ binary_to_list(DbName) ++ "_temp"),
             true = ets:delete(couch_views_temp_fd_by_db, DbName);
         _ ->
             true = ets:insert(couch_views_temp_fd_by_db, {DbName, Fd, Count - 1})
@@ -398,7 +405,8 @@
         delete_index_file(RootDir, DbName, GroupId),
         exit(Else)
     end,
-    FileName = RootDir ++ "/." ++ DbName ++ GroupId ++".view",
+    FileName = RootDir ++ "/." ++ binary_to_list(DbName) ++ 
+            binary_to_list(GroupId) ++".view",
     Group2 =
     case couch_file:open(FileName) of
     {ok, Fd} ->
@@ -481,7 +489,6 @@
             fun(DocInfo, _, Acc) -> process_doc(Db, DocInfo, Acc) end,
             {[], Group, ViewEmptyKVs, [], CurrentSeq}
             ),
-
     {Group3, Results} = view_compute(Group2, UncomputedDocs),
     {ViewKVsToAdd2, DocIdViewIdKeys2} = view_insert_query_results(UncomputedDocs, Results, ViewKVsToAdd, DocIdViewIdKeys),
     couch_query_servers:stop_doc_map(Group3#group.query_server),
@@ -493,7 +500,7 @@
     end.
     
 delete_index_dir(RootDir, DbName) ->
-    nuke_dir(RootDir ++ "/." ++ DbName ++ "_design").
+    nuke_dir(RootDir ++ "/." ++ binary_to_list(DbName) ++ "_design").
 
 nuke_dir(Dir) ->
     case file:list_dir(Dir) of
@@ -513,7 +520,8 @@
     end.
 
 delete_index_file(RootDir, DbName, GroupId) ->
-    file:delete(RootDir ++ "/." ++ DbName ++ GroupId ++ ".view").
+    file:delete(RootDir ++ "/." ++ binary_to_list(DbName)
+            ++ binary_to_list(GroupId) ++ ".view").
 
 init_group(Db, Fd, #group{views=Views}=Group, nil = _IndexHeaderData) ->
     init_group(Db, Fd, Group, {0, nil, [nil || _ <- Views]});
@@ -526,8 +534,9 @@
             ReduceFun = 
                 fun(reduce, KVs) ->
                     KVs2 = expand_dups(KVs,[]),
-                    {ok, Reduced} = couch_query_servers:reduce(Lang, FunSrcs, KVs2),
-                    {length(KVs2), Reduced};
+                    KVs3 = detuple_kvs(KVs2,[]),
+                    {ok, Reduced} = couch_query_servers:reduce(Lang, FunSrcs, KVs3),
+                    {length(KVs3), Reduced};
                 (rereduce, Reds) ->
                     Count = lists:sum([Count0 || {Count0, _} <- Reds]),
                     UserReds = [UserRedsList || {_, UserRedsList} <- Reds],
@@ -535,7 +544,7 @@
                     {Count, Reduced}
                 end,
             {ok, Btree} = couch_btree:open(BtreeState, Fd,
-                        [{less, fun less_json/2},{reduce, ReduceFun}]),
+                        [{less, fun less_json_keys/2},{reduce, ReduceFun}]),
             View#view{btree=Btree}
         end,
         ViewStates, Views),
@@ -546,14 +555,17 @@
     ViewStates = [couch_btree:get_state(Btree) || #view{btree=Btree} <- Views],
     {Seq, couch_btree:get_state(IdBtree), ViewStates}.
 
-
+% keys come back in the language of btree - tuples.
+less_json_keys(A, B) ->
+    less_json(tuple_to_list(A), tuple_to_list(B)).
 
 less_json(A, B) ->
     TypeA = type_sort(A),
     TypeB = type_sort(B),
     if
     TypeA == TypeB ->
-        less_same_type(A,B);
+        Less = less_same_type(A,B),
+        Less;
     true ->
         TypeA < TypeB
     end.
@@ -561,36 +573,35 @@
 type_sort(V) when is_atom(V) -> 0;
 type_sort(V) when is_integer(V) -> 1;
 type_sort(V) when is_float(V) -> 1;
-type_sort(V) when is_list(V) -> 2;
-type_sort({obj, _}) -> 4; % must come before tuple test below
-type_sort(V) when is_tuple(V) -> 3;
-type_sort(V) when is_binary(V) -> 5.
+type_sort(V) when is_binary(V) -> 2;
+type_sort(V) when is_list(V) -> 3;
+type_sort({V}) when is_list(V) -> 4; % must come before tuple test below
+type_sort(V) when is_tuple(V) -> 5.
+
 
 atom_sort(nil) -> 0;
 atom_sort(null) -> 1;
 atom_sort(false) -> 2;
 atom_sort(true) -> 3.
 
+
 less_same_type(A,B) when is_atom(A) ->
-    atom_sort(A) < atom_sort(B);
-less_same_type(A,B) when is_list(A) ->
-    couch_util:collate(A, B) < 0;
-less_same_type({obj, AProps}, {obj, BProps}) ->
-    less_props(AProps, BProps);
-less_same_type(A, B) when is_tuple(A) ->
-    less_list(tuple_to_list(A),tuple_to_list(B));
+  atom_sort(A) < atom_sort(B);
+less_same_type(A,B) when is_binary(A) ->
+  couch_util:collate(A, B) < 0;
+less_same_type({AProps}, {BProps}) ->
+  less_props(AProps, BProps);
+less_same_type(A, B) when is_list(A) ->
+  less_list(A, B);
 less_same_type(A, B) ->
     A < B.
-
-ensure_list(V) when is_list(V) -> V;
-ensure_list(V) when is_atom(V) -> atom_to_list(V).
-
+	
 less_props([], [_|_]) ->
     true;
 less_props(_, []) ->
     false;
 less_props([{AKey, AValue}|RestA], [{BKey, BValue}|RestB]) ->
-    case couch_util:collate(ensure_list(AKey), ensure_list(BKey)) of
+    case couch_util:collate(AKey, BKey) of
     -1 -> true;
     1 -> false;
     0 ->
@@ -639,7 +650,7 @@
         {not_found, deleted} ->
             throw(restart)
         end;
-    ?DESIGN_DOC_PREFIX ++ _ -> % we skip design docs
+    <<?DESIGN_DOC_PREFIX, _>> -> % we skip design docs
         {ok, {Docs, Group, ViewKVs, DocIdViewIdKeys, Seq}};
     _ ->
         {Docs2, DocIdViewIdKeys2} =
@@ -708,7 +719,6 @@
         {ok, QueryServerIn}
     end,
     {ok, Results} = couch_query_servers:map_docs(QueryServer, Docs),
-    
     {Group#group{query_server=QueryServer}, Results}.
 
 
@@ -726,7 +736,6 @@
     AddDocIdViewIdKeys = [{DocId, ViewIdKeys} || {DocId, ViewIdKeys} <- DocIdViewIdKeys, ViewIdKeys /= []],
     RemoveDocIds = [DocId || {DocId, ViewIdKeys} <- DocIdViewIdKeys, ViewIdKeys == []],
     LookupDocIds = [DocId || {DocId, _ViewIdKeys} <- DocIdViewIdKeys],
-
     {ok, LookupResults, IdBtree2}
         = couch_btree:query_modify(IdBtree, LookupDocIds, AddDocIdViewIdKeys, RemoveDocIds),
     KeysToRemoveByView = lists:foldl(