From 52804ef907f5c0c4cf9ea45c42714502ba46093f Mon Sep 17 00:00:00 2001
From: Filipe David Borba Manana <fdmanana@apache.org>
Date: Mon, 13 Feb 2012 15:41:14 +0000
Subject: [PATCH] Only validate numbers on JSON decoding

Let the encoder validate only the numbers and encode them
as the term {json, RawNumberJsonBinary}.

COUCHDB-1407
---
 src/couchdb/couch_db.hrl            |    1 +
 src/couchdb/couch_httpd.erl         |    2 +-
 src/couchdb/couch_httpd_db.erl      |    2 +-
 src/couchdb/couch_query_servers.erl |    2 +-
 src/ejson/ejson.erl                 |   60 ++++++++++++++++++++++------------
 5 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
index 65eb7f0..6fae38c 100644
--- a/src/couchdb/couch_db.hrl
+++ b/src/couchdb/couch_db.hrl
@@ -23,6 +23,7 @@
 
 -define(JSON_ENCODE(V), ejson:encode(V)).
 -define(JSON_DECODE(V), ejson:decode(V)).
+-define(JSON_DECODE_RAW_NUMBERS(V), ejson:decode(V, [raw_numbers])).
 
 -define(b2l(V), binary_to_list(V)).
 -define(l2b(V), list_to_binary(V)).
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index 8b05076..a9dd65e 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -556,7 +556,7 @@ body(#httpd{req_body=ReqBody}) ->
     ReqBody.
 
 json_body(Httpd) ->
-    ?JSON_DECODE(body(Httpd)).
+    ?JSON_DECODE_RAW_NUMBERS(body(Httpd)).
 
 json_body_obj(Httpd) ->
     case json_body(Httpd) of
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index f669643..f732401 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -509,7 +509,7 @@ db_doc_req(#httpd{method='POST'}=Req, Db, DocId) ->
         Rev = couch_doc:parse_rev(couch_util:get_value("_rev", Form)),
         {ok, [{ok, Doc}]} = couch_db:open_doc_revs(Db, DocId, [Rev], []);
     Json ->
-        Doc = couch_doc_from_req(Req, DocId, ?JSON_DECODE(Json))
+        Doc = couch_doc_from_req(Req, DocId, ?JSON_DECODE_RAW_NUMBERS(Json))
     end,
     UpdatedAtts = [
         #att{name=validate_attachment_name(Name),
diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl
index e29f23b..e757edf 100644
--- a/src/couchdb/couch_query_servers.erl
+++ b/src/couchdb/couch_query_servers.erl
@@ -526,7 +526,7 @@ proc_prompt_raw(#proc{prompt_fun = {Mod, Func}} = Proc, Args) ->
     apply(Mod, Func, [Proc#proc.pid, Args]).
 
 raw_to_ejson({json, Json}) ->
-    ?JSON_DECODE(Json);
+    ?JSON_DECODE_RAW_NUMBERS(Json);
 raw_to_ejson(EJson) ->
     EJson.
 
diff --git a/src/ejson/ejson.erl b/src/ejson/ejson.erl
index 07a71c2..85e155b 100644
--- a/src/ejson/ejson.erl
+++ b/src/ejson/ejson.erl
@@ -11,7 +11,7 @@
 % the License.
 
 -module(ejson).
--export([encode/1, decode/1]).
+-export([encode/1, decode/1, decode/2]).
 -on_load(init/0).
 
 init() ->
@@ -34,8 +34,11 @@ init() ->
 
 
 decode(IoList) ->
+    decode(IoList, []).
+
+decode(IoList, Options) ->
     try
-        nif_decode(IoList)
+        nif_decode(IoList, Options)
     catch exit:ejson_nif_not_loaded ->
         erl_decode(IoList)
     end.
@@ -48,10 +51,11 @@ encode(EJson) ->
     end.
 
 
-nif_decode(IoList) ->
+nif_decode(IoList, Options) ->
     case reverse_tokens(IoList) of
     {ok, ReverseTokens} ->
-        [[EJson]] = make_ejson(ReverseTokens, [[]]),
+        RawNumbers = lists:member(raw_numbers, Options),
+        [[EJson]] = make_ejson(ReverseTokens, [[]], RawNumbers),
         EJson;
     Error ->
         throw({invalid_json, {Error, IoList}})
@@ -85,6 +89,8 @@ mochi_encode_handler(Bad) ->
 % everything in the list is the final output except for tuples with
 % {0, Strings} and {1, Floats}, which are to be converted to strings
 % inside the NIF.
+encode_rev({json, RawJson}) ->
+    RawJson;
 encode_rev(true) ->
     <<"true">>;
 encode_rev(false) ->
@@ -132,31 +138,43 @@ as_binary(L) when is_list(L) ->
     list_to_binary(L).
 
 
-make_ejson([], Stack) ->
+make_ejson([], Stack, _RawNumbers) ->
     Stack;
-make_ejson([0 | RevEvs], [ArrayValues, PrevValues | RestStack]) ->
+make_ejson([0 | RevEvs], [ArrayValues, PrevValues | RestStack], RawNumbers) ->
     % 0 ArrayStart
-    make_ejson(RevEvs, [[ArrayValues | PrevValues] | RestStack]);
-make_ejson([1 | RevEvs], Stack) ->
+    make_ejson(RevEvs, [[ArrayValues | PrevValues] | RestStack], RawNumbers);
+make_ejson([1 | RevEvs], Stack, RawNumbers) ->
     % 1 ArrayEnd
-    make_ejson(RevEvs, [[] | Stack]);
-make_ejson([2 | RevEvs], [ObjValues, PrevValues | RestStack]) ->
+    make_ejson(RevEvs, [[] | Stack], RawNumbers);
+make_ejson([2 | RevEvs], [ObjValues, PrevValues | RestStack], RawNumbers) ->
     % 2 ObjectStart
-    make_ejson(RevEvs, [[{ObjValues} | PrevValues] | RestStack]);
-make_ejson([3 | RevEvs], Stack) ->
+    make_ejson(RevEvs, [[{ObjValues} | PrevValues] | RestStack], RawNumbers);
+make_ejson([3 | RevEvs], Stack, RawNumbers) ->
     % 3 ObjectEnd
-    make_ejson(RevEvs, [[] | Stack]);
-make_ejson([{0, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
+    make_ejson(RevEvs, [[] | Stack], RawNumbers);
+make_ejson([{0, Value} | RevEvs], [Vals | RestStack] = _Stack, RawNumbers) ->
     % {0, IntegerString}
-    make_ejson(RevEvs, [[list_to_integer(binary_to_list(Value)) | Vals] | RestStack]);
-make_ejson([{1, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
+    EncValue = case RawNumbers of
+    true ->
+        {json, Value};
+    false ->
+        list_to_integer(binary_to_list(Value))
+    end,
+    make_ejson(RevEvs, [[EncValue | Vals] | RestStack], RawNumbers);
+make_ejson([{1, Value} | RevEvs], [Vals | RestStack] = _Stack, RawNumbers) ->
     % {1, FloatString}
-    make_ejson(RevEvs, [[list_to_float(binary_to_list(Value)) | Vals] | RestStack]);
-make_ejson([{3, String} | RevEvs], [[PrevValue|RestObject] | RestStack] = _Stack) ->
+    EncValue = case RawNumbers of
+    true ->
+        {json, Value};
+    false ->
+        list_to_float(binary_to_list(Value))
+    end,
+    make_ejson(RevEvs, [[EncValue | Vals] | RestStack], RawNumbers);
+make_ejson([{3, String} | RevEvs], [[PrevValue|RestObject] | RestStack] = _Stack, RawNumbers) ->
     % {3 , ObjectKey}
-    make_ejson(RevEvs, [[{String, PrevValue}|RestObject] | RestStack]);
-make_ejson([Value | RevEvs], [Vals | RestStack] = _Stack) ->
-    make_ejson(RevEvs, [[Value | Vals] | RestStack]).
+    make_ejson(RevEvs, [[{String, PrevValue}|RestObject] | RestStack], RawNumbers);
+make_ejson([Value | RevEvs], [Vals | RestStack] = _Stack, RawNumbers) ->
+    make_ejson(RevEvs, [[Value | Vals] | RestStack], RawNumbers).
 
 
 reverse_tokens(_) ->
-- 
1.7.4.4
