Skip to content

Commit 1779125

Browse files
rhegdeChris Elder
rhegde
authored and
Chris Elder
committed
[FAB-2911]Fix JSON-unmarshal for Generic Interface
GOLANG JSON package stores integer in float format. In fabric Couch DB state ledger code performs a json unmarshalling on the data wrapper using generic interface data-type. For JSON document stored in the state ledger that has integer with digit size >=7; now gets unmarshalled with a E (float) notation which is eventually passed to the user chain-code upon for eg. shim GetState API. Unmarshalling at the user-chaincode level result in an error indicating float cannot be converted to integer type. To fix this issue, it is neccessary to use NewDecoder API of JSON package and retains the number representation in such case. Change-Id: I650cf88dacff84e5e9a185bfa44984a2814488a4 Signed-off-by: rhegde <[email protected]>
1 parent 561443e commit 1779125

File tree

5 files changed

+65
-15
lines changed

5 files changed

+65
-15
lines changed

core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go

+35-12
Original file line numberDiff line numberDiff line change
@@ -208,20 +208,22 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) {
208208
batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9))
209209
jsonValue10 := "{\"asset_name\": \"marble10\",\"color\": \"green\",\"size\": 10,\"owner\": \"mary\"}"
210210
batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10))
211+
jsonValue11 := "{\"asset_name\": \"marble11\",\"color\": \"cyan\",\"size\": 1000007,\"owner\": \"joe\"}"
212+
batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11))
211213

212214
//add keys for a separate namespace
213-
batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 11))
214-
batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 12))
215-
batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 13))
216-
batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 14))
217-
batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 15))
218-
batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 16))
219-
batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 17))
220-
batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 18))
221-
batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 19))
222-
batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 20))
223-
224-
savePoint := version.NewHeight(2, 21)
215+
batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 12))
216+
batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 13))
217+
batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 14))
218+
batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 15))
219+
batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 16))
220+
batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 17))
221+
batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 18))
222+
batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 19))
223+
batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 20))
224+
batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 21))
225+
226+
savePoint := version.NewHeight(2, 22)
225227
db.ApplyUpdates(batch, savePoint)
226228

227229
// query for owner=jerry, use namespace "ns1"
@@ -435,4 +437,25 @@ func TestQuery(t *testing.T, dbProvider statedb.VersionedDBProvider) {
435437
testutil.AssertNoError(t, err, "")
436438
testutil.AssertNil(t, queryResult1)
437439

440+
// query with integer with digit-count equals 7 and response received is also received
441+
// with same digit-count and there is no float transformation
442+
itr, err = db.ExecuteQuery("ns1", "{\"selector\":{\"$and\":[{\"size\":{\"$eq\": 1000007}}]}}")
443+
testutil.AssertNoError(t, err, "")
444+
445+
// verify one jerry result
446+
queryResult1, err = itr.Next()
447+
testutil.AssertNoError(t, err, "")
448+
testutil.AssertNotNil(t, queryResult1)
449+
versionedQueryRecord = queryResult1.(*statedb.VersionedQueryRecord)
450+
stringRecord = string(versionedQueryRecord.Record)
451+
bFoundRecord = strings.Contains(stringRecord, "joe")
452+
testutil.AssertEquals(t, bFoundRecord, true)
453+
bFoundRecord = strings.Contains(stringRecord, "1000007")
454+
testutil.AssertEquals(t, bFoundRecord, true)
455+
456+
// verify no more results
457+
queryResult2, err = itr.Next()
458+
testutil.AssertNoError(t, err, "")
459+
testutil.AssertNil(t, queryResult2)
460+
438461
}

core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package statecouchdb
1818

1919
import (
20+
"bytes"
2021
"encoding/json"
2122
"fmt"
2223
"reflect"
@@ -66,7 +67,9 @@ func ApplyQueryWrapper(namespace, queryString string, queryLimit, querySkip int)
6667
jsonQueryMap := make(map[string]interface{})
6768

6869
//unmarshal the selected json into the generic map
69-
err := json.Unmarshal([]byte(queryString), &jsonQueryMap)
70+
decoder := json.NewDecoder(bytes.NewBuffer([]byte(queryString)))
71+
decoder.UseNumber()
72+
err := decoder.Decode(&jsonQueryMap)
7073
if err != nil {
7174
return "", err
7275
}
@@ -167,6 +170,10 @@ func processAndWrapQuery(jsonQueryMap map[string]interface{}) {
167170
//intercept the float64 case and prevent the []interface{} case from
168171
//incorrectly processing the float64
169172

173+
case json.Number:
174+
//intercept the Number case and prevent the []interface{} case from
175+
//incorrectly processing the float64
176+
170177
//if the type is an array, then iterate through the items
171178
case []interface{}:
172179

core/ledger/kvledger/txmgmt/statedb/statecouchdb/query_wrapper_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,18 @@ func TestQueryWithUseDesignDocAndIndexName(t *testing.T) {
366366
testutil.AssertEquals(t, strings.Count(wrappedQuery, "\"use_index\":[\"_design/testDoc\",\"testIndexName\"]"), 1)
367367

368368
}
369+
370+
//TestQueryWithLargeInteger tests query with large integer
371+
func TestQueryWithLargeInteger(t *testing.T) {
372+
373+
rawQuery := []byte(`{"selector":{"$and":[{"size":{"$eq": 1000007}}]}}`)
374+
375+
wrappedQuery, err := ApplyQueryWrapper("ns1", string(rawQuery), 10000, 0)
376+
377+
//Make sure the query did not throw an exception
378+
testutil.AssertNoError(t, err, "Unexpected error thrown when for query JSON")
379+
380+
//check to make sure the default selector is added
381+
testutil.AssertEquals(t, strings.Count(wrappedQuery, "{\"$eq\":1000007}"), 1)
382+
383+
}

core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ func removeDataWrapper(wrappedValue []byte, attachments []couchdb.Attachment) ([
144144
jsonResult := make(map[string]interface{})
145145

146146
//unmarshal the selected json into the generic map
147-
json.Unmarshal(wrappedValue, &jsonResult)
147+
decoder := json.NewDecoder(bytes.NewBuffer(wrappedValue))
148+
decoder.UseNumber()
149+
_ = decoder.Decode(&jsonResult)
148150

149151
// handle binary or json data
150152
if jsonResult[dataWrapper] == nil && attachments != nil { // binary attachment

core/ledger/util/couchdb/couchdb.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,11 @@ func createAttachmentPart(couchDoc *CouchDoc, defaultBoundary string) (bytes.Buf
504504

505505
//create a generic map
506506
genericMap := make(map[string]interface{})
507+
507508
//unmarshal the data into the generic map
508-
json.Unmarshal(couchDoc.JSONValue, &genericMap)
509+
decoder := json.NewDecoder(bytes.NewBuffer(couchDoc.JSONValue))
510+
decoder.UseNumber()
511+
decoder.Decode(&genericMap)
509512

510513
//add all key/values to the attachmentJSONMap
511514
for jsonKey, jsonValue := range genericMap {

0 commit comments

Comments
 (0)