Skip to content

Commit e5bd121

Browse files
committed
Fix composite key range queries on CouchDB
Use an order preserving encoding for composite keys using UTF-8 so that range queries work on CouchDB, and return results in sorted order. Change-Id: Iadc6398e7f6efafd4259371e9f5f948723e167a7 Signed-off-by: denyeart <[email protected]>
1 parent a429da3 commit e5bd121

File tree

2 files changed

+30
-20
lines changed

2 files changed

+30
-20
lines changed

core/chaincode/shim/chaincode.go

+17-17
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,20 @@ limitations under the License.
1919
package shim
2020

2121
import (
22+
"bytes"
2223
"errors"
2324
"flag"
2425
"fmt"
2526
"io"
2627
"os"
28+
"regexp"
29+
"strconv"
2730
"strings"
2831

2932
"github.com/golang/protobuf/proto"
3033
"github.com/golang/protobuf/ptypes/timestamp"
3134
"github.com/hyperledger/fabric/common/util"
3235
"github.com/hyperledger/fabric/core/comm"
33-
coder "github.com/hyperledger/fabric/core/ledger/util"
3436
pb "github.com/hyperledger/fabric/protos/peer"
3537
"github.com/op/go-logging"
3638
"github.com/spf13/viper"
@@ -356,14 +358,14 @@ func (stub *ChaincodeStub) CreateCompositeKey(objectType string, attributes []st
356358
}
357359

358360
func createCompositeKey(stub ChaincodeStubInterface, objectType string, attributes []string) (string, error) {
359-
var compositeKey []byte
360-
compositeKey = append(compositeKey, coder.EncodeOrderPreservingVarUint64(uint64(len(objectType)))...)
361-
compositeKey = append(compositeKey, []byte(objectType)...)
361+
var compositeKey bytes.Buffer
362+
replacer := strings.NewReplacer("\x1E", "\x1E\x1E", "\x1F", "\x1E\x1F")
363+
compositeKey.WriteString(replacer.Replace(objectType))
362364
for _, attribute := range attributes {
363-
compositeKey = append(compositeKey, coder.EncodeOrderPreservingVarUint64(uint64(len(attribute)))...)
364-
compositeKey = append(compositeKey, []byte(attribute)...)
365+
compositeKey.WriteString("\x1F" + strconv.Itoa(len(attribute)) + "\x1F")
366+
compositeKey.WriteString(replacer.Replace(attribute))
365367
}
366-
return string(compositeKey), nil
368+
return compositeKey.String(), nil
367369
}
368370

369371
//Given a composite key, SplitCompositeKey function splits the key into attributes
@@ -373,17 +375,15 @@ func (stub *ChaincodeStub) SplitCompositeKey(compositeKey string) (string, []str
373375
}
374376

375377
func splitCompositeKey(stub ChaincodeStubInterface, compositeKey string) (string, []string, error) {
376-
startIndex := 0
377-
cKey := []byte(compositeKey)
378-
attributes := []string{}
379-
for startIndex < len(compositeKey) {
380-
len, bytesConsumed := coder.DecodeOrderPreservingVarUint64(cKey[startIndex:])
381-
attrBeginIndex := startIndex + int(bytesConsumed)
382-
attrEndIndex := attrBeginIndex + int(len)
383-
attributes = append(attributes, compositeKey[attrBeginIndex:attrEndIndex])
384-
startIndex = attrEndIndex
378+
re := regexp.MustCompile("\x1F[0-9]+\x1F")
379+
splittedKey := re.Split(compositeKey, -1)
380+
attributes := make([]string, 0)
381+
replacer := strings.NewReplacer("\x1E\x1F", "\x1F", "\x1E\x1E", "\x1E")
382+
objectType := replacer.Replace(splittedKey[0])
383+
for _, attr := range splittedKey[1:] {
384+
attributes = append(attributes, replacer.Replace(attr))
385385
}
386-
return attributes[0], attributes[1:], nil
386+
return objectType, attributes, nil
387387
}
388388

389389
//PartialCompositeKeyQuery function can be invoked by a chaincode to query the

core/ledger/util/couchdb/couchdb.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -645,15 +645,25 @@ func (dbclient *CouchDatabase) ReadDocRange(startKey, endKey string, limit, skip
645645
//Append the startKey if provided
646646
if startKey != "" {
647647
startKey = strconv.QuoteToGraphic(startKey)
648-
startKey = strings.Replace(startKey, "\\x00", "\\u0000", 1)
648+
startKey = strings.Replace(startKey, "\\x00", "\\u0000", -1)
649+
startKey = strings.Replace(startKey, "\\x1e", "\\u001e", -1)
650+
startKey = strings.Replace(startKey, "\\x1f", "\\u001f", -1)
651+
startKey = strings.Replace(startKey, "\\xff", "\\u00ff", -1)
652+
//TODO add general unicode support instead of special cases
653+
649654
queryParms.Add("startkey", startKey)
650655
}
651656

652657
//Append the endKey if provided
653658
if endKey != "" {
654659
endKey = strconv.QuoteToGraphic(endKey)
655-
endKey = strings.Replace(endKey, "\\x00", "\\u0000", 1)
656-
endKey = strings.Replace(endKey, "\\x01", "\\u0001", 1) //TODO add general unicode support instead of special cases
660+
endKey = strings.Replace(endKey, "\\x00", "\\u0000", -1)
661+
endKey = strings.Replace(endKey, "\\x01", "\\u0001", -1)
662+
endKey = strings.Replace(endKey, "\\x1e", "\\u001e", -1)
663+
endKey = strings.Replace(endKey, "\\x1f", "\\u001f", -1)
664+
endKey = strings.Replace(endKey, "\\xff", "\\u00ff", -1)
665+
//TODO add general unicode support instead of special cases
666+
657667
queryParms.Add("endkey", endKey)
658668
}
659669

0 commit comments

Comments
 (0)