github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/txmgr/query_executor.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package txmgr
     8  
     9  import (
    10  	commonledger "github.com/hechain20/hechain/common/ledger"
    11  	"github.com/hechain20/hechain/core/ledger"
    12  	"github.com/hechain20/hechain/core/ledger/internal/version"
    13  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil"
    14  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb"
    15  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statemetadata"
    16  	"github.com/hechain20/hechain/core/ledger/util"
    17  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
    18  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  const (
    23  	queryReadsHashingEnabled   = true
    24  	maxDegreeQueryReadsHashing = uint32(50)
    25  )
    26  
    27  // queryExecutor is a query executor used in `LockBasedTxMgr`
    28  type queryExecutor struct {
    29  	txmgr             *LockBasedTxMgr
    30  	collNameValidator *collNameValidator
    31  	collectReadset    bool
    32  	rwsetBuilder      *rwsetutil.RWSetBuilder
    33  	itrs              []*resultsItr
    34  	err               error
    35  	doneInvoked       bool
    36  	hasher            rwsetutil.HashFunc
    37  	txid              string
    38  	privateReads      *ledger.PrivateReads
    39  }
    40  
    41  func newQueryExecutor(txmgr *LockBasedTxMgr,
    42  	txid string,
    43  	rwsetBuilder *rwsetutil.RWSetBuilder,
    44  	performCollCheck bool,
    45  	hashFunc rwsetutil.HashFunc) *queryExecutor {
    46  	logger.Debugf("constructing new query executor txid = [%s]", txid)
    47  	qe := &queryExecutor{}
    48  	qe.txid = txid
    49  	qe.txmgr = txmgr
    50  	if rwsetBuilder != nil {
    51  		qe.collectReadset = true
    52  		qe.rwsetBuilder = rwsetBuilder
    53  	}
    54  	qe.hasher = hashFunc
    55  	validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, qe, !performCollCheck)
    56  	qe.collNameValidator = validator
    57  	qe.privateReads = &ledger.PrivateReads{}
    58  	return qe
    59  }
    60  
    61  // GetState implements method in interface `ledger.QueryExecutor`
    62  func (q *queryExecutor) GetState(ns, key string) ([]byte, error) {
    63  	val, _, err := q.getState(ns, key)
    64  	return val, err
    65  }
    66  
    67  func (q *queryExecutor) getState(ns, key string) ([]byte, []byte, error) {
    68  	if err := q.checkDone(); err != nil {
    69  		return nil, nil, err
    70  	}
    71  	versionedValue, err := q.txmgr.db.GetState(ns, key)
    72  	if err != nil {
    73  		return nil, nil, err
    74  	}
    75  	val, metadata, ver := decomposeVersionedValue(versionedValue)
    76  	if q.collectReadset {
    77  		q.rwsetBuilder.AddToReadSet(ns, key, ver)
    78  	}
    79  	return val, metadata, nil
    80  }
    81  
    82  // GetStateMetadata implements method in interface `ledger.QueryExecutor`
    83  func (q *queryExecutor) GetStateMetadata(ns, key string) (map[string][]byte, error) {
    84  	if err := q.checkDone(); err != nil {
    85  		return nil, err
    86  	}
    87  	var metadata []byte
    88  	var err error
    89  	if !q.collectReadset {
    90  		if metadata, err = q.txmgr.db.GetStateMetadata(ns, key); err != nil {
    91  			return nil, err
    92  		}
    93  	} else {
    94  		if _, metadata, err = q.getState(ns, key); err != nil {
    95  			return nil, err
    96  		}
    97  	}
    98  	return statemetadata.Deserialize(metadata)
    99  }
   100  
   101  // GetStateMultipleKeys implements method in interface `ledger.QueryExecutor`
   102  func (q *queryExecutor) GetStateMultipleKeys(ns string, keys []string) ([][]byte, error) {
   103  	if err := q.checkDone(); err != nil {
   104  		return nil, err
   105  	}
   106  	versionedValues, err := q.txmgr.db.GetStateMultipleKeys(ns, keys)
   107  	if err != nil {
   108  		return nil, nil
   109  	}
   110  	values := make([][]byte, len(versionedValues))
   111  	for i, versionedValue := range versionedValues {
   112  		val, _, ver := decomposeVersionedValue(versionedValue)
   113  		if q.collectReadset {
   114  			q.rwsetBuilder.AddToReadSet(ns, keys[i], ver)
   115  		}
   116  		values[i] = val
   117  	}
   118  	return values, nil
   119  }
   120  
   121  // GetStateRangeScanIterator implements method in interface `ledger.QueryExecutor`
   122  // startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key
   123  // and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey
   124  // can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons.
   125  func (q *queryExecutor) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (commonledger.ResultsIterator, error) {
   126  	if err := q.checkDone(); err != nil {
   127  		return nil, err
   128  	}
   129  	itr, err := newResultsItr(
   130  		namespace,
   131  		startKey,
   132  		endKey,
   133  		0,
   134  		q.txmgr.db,
   135  		q.rwsetBuilder,
   136  		queryReadsHashingEnabled,
   137  		maxDegreeQueryReadsHashing,
   138  		q.hasher,
   139  	)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	q.itrs = append(q.itrs, itr)
   144  	return itr, nil
   145  }
   146  
   147  func (q *queryExecutor) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (ledger.QueryResultsIterator, error) {
   148  	if err := q.checkDone(); err != nil {
   149  		return nil, err
   150  	}
   151  	itr, err := newResultsItr(
   152  		namespace,
   153  		startKey,
   154  		endKey,
   155  		pageSize,
   156  		q.txmgr.db,
   157  		q.rwsetBuilder,
   158  		queryReadsHashingEnabled,
   159  		maxDegreeQueryReadsHashing,
   160  		q.hasher,
   161  	)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	q.itrs = append(q.itrs, itr)
   166  	return itr, nil
   167  }
   168  
   169  // ExecuteQuery implements method in interface `ledger.QueryExecutor`
   170  func (q *queryExecutor) ExecuteQuery(namespace, query string) (commonledger.ResultsIterator, error) {
   171  	if err := q.checkDone(); err != nil {
   172  		return nil, err
   173  	}
   174  	dbItr, err := q.txmgr.db.ExecuteQuery(namespace, query)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	return &queryResultsItr{DBItr: dbItr, RWSetBuilder: q.rwsetBuilder}, nil
   179  }
   180  
   181  func (q *queryExecutor) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (ledger.QueryResultsIterator, error) {
   182  	if err := q.checkDone(); err != nil {
   183  		return nil, err
   184  	}
   185  	dbItr, err := q.txmgr.db.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	return &queryResultsItr{DBItr: dbItr, RWSetBuilder: q.rwsetBuilder}, nil
   190  }
   191  
   192  // GetPrivateData implements method in interface `ledger.QueryExecutor`
   193  func (q *queryExecutor) GetPrivateData(ns, coll, key string) ([]byte, error) {
   194  	if err := q.validateCollName(ns, coll); err != nil {
   195  		return nil, err
   196  	}
   197  	if err := q.checkDone(); err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	var err error
   202  	var hashVersion *version.Height
   203  	var versionedValue *statedb.VersionedValue
   204  
   205  	if versionedValue, err = q.txmgr.db.GetPrivateData(ns, coll, key); err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	// metadata is always nil for private data - because, the metadata is part of the hashed key (instead of raw key)
   210  	val, _, ver := decomposeVersionedValue(versionedValue)
   211  
   212  	keyHash := util.ComputeStringHash(key)
   213  	if hashVersion, err = q.txmgr.db.GetKeyHashVersion(ns, coll, keyHash); err != nil {
   214  		return nil, err
   215  	}
   216  	if !version.AreSame(hashVersion, ver) {
   217  		return nil, errors.Errorf(
   218  			"private data matching public hash version is not available. Public hash version = %s, Private data version = %s",
   219  			hashVersion, ver,
   220  		)
   221  	}
   222  	if q.collectReadset {
   223  		q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   224  		q.privateReads.Add(ns, coll)
   225  	}
   226  	return val, nil
   227  }
   228  
   229  func (q *queryExecutor) GetPrivateDataHash(ns, coll, key string) ([]byte, error) {
   230  	if err := q.validateCollName(ns, coll); err != nil {
   231  		return nil, err
   232  	}
   233  	if err := q.checkDone(); err != nil {
   234  		return nil, err
   235  	}
   236  	var versionedValue *statedb.VersionedValue
   237  	var err error
   238  	if versionedValue, err = q.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil {
   239  		return nil, err
   240  	}
   241  	valHash, _, ver := decomposeVersionedValue(versionedValue)
   242  	if q.collectReadset {
   243  		q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   244  	}
   245  	return valHash, nil
   246  }
   247  
   248  // GetPrivateDataMetadata implements method in interface `ledger.QueryExecutor`
   249  func (q *queryExecutor) GetPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) {
   250  	if !q.collectReadset {
   251  		// reads versions are not getting recorded, retrieve metadata value via optimized path
   252  		return q.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key))
   253  	}
   254  	if err := q.validateCollName(ns, coll); err != nil {
   255  		return nil, err
   256  	}
   257  	if err := q.checkDone(); err != nil {
   258  		return nil, err
   259  	}
   260  	_, metadataBytes, err := q.getPrivateDataValueHash(ns, coll, key)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	return statemetadata.Deserialize(metadataBytes)
   265  }
   266  
   267  func (q *queryExecutor) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) {
   268  	if err := q.validateCollName(ns, coll); err != nil {
   269  		return nil, nil, err
   270  	}
   271  	if err := q.checkDone(); err != nil {
   272  		return nil, nil, err
   273  	}
   274  	var versionedValue *statedb.VersionedValue
   275  	if versionedValue, err = q.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil {
   276  		return nil, nil, err
   277  	}
   278  	valHash, metadata, ver := decomposeVersionedValue(versionedValue)
   279  	if q.collectReadset {
   280  		q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   281  	}
   282  	return valHash, metadata, nil
   283  }
   284  
   285  // GetPrivateDataMetadataByHash implements method in interface `ledger.QueryExecutor`
   286  func (q *queryExecutor) GetPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) {
   287  	return q.getPrivateDataMetadataByHash(ns, coll, keyhash)
   288  }
   289  
   290  func (q *queryExecutor) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) {
   291  	if err := q.validateCollName(ns, coll); err != nil {
   292  		return nil, err
   293  	}
   294  	if err := q.checkDone(); err != nil {
   295  		return nil, err
   296  	}
   297  	if q.collectReadset {
   298  		// this requires to improve rwset builder to accept a keyhash
   299  		return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet")
   300  	}
   301  	metadataBytes, err := q.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	return statemetadata.Deserialize(metadataBytes)
   306  }
   307  
   308  // GetPrivateDataMultipleKeys implements method in interface `ledger.QueryExecutor`
   309  func (q *queryExecutor) GetPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) {
   310  	if err := q.validateCollName(ns, coll); err != nil {
   311  		return nil, err
   312  	}
   313  	if err := q.checkDone(); err != nil {
   314  		return nil, err
   315  	}
   316  	versionedValues, err := q.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys)
   317  	if err != nil {
   318  		return nil, nil
   319  	}
   320  	values := make([][]byte, len(versionedValues))
   321  	for i, versionedValue := range versionedValues {
   322  		val, _, ver := decomposeVersionedValue(versionedValue)
   323  		if q.collectReadset {
   324  			q.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver)
   325  			q.privateReads.Add(ns, coll)
   326  		}
   327  		values[i] = val
   328  	}
   329  	return values, nil
   330  }
   331  
   332  // GetPrivateDataRangeScanIterator implements method in interface `ledger.QueryExecutor`
   333  func (q *queryExecutor) GetPrivateDataRangeScanIterator(ns, coll, startKey, endKey string) (commonledger.ResultsIterator, error) {
   334  	if err := q.validateCollName(ns, coll); err != nil {
   335  		return nil, err
   336  	}
   337  	if err := q.checkDone(); err != nil {
   338  		return nil, err
   339  	}
   340  	dbItr, err := q.txmgr.db.GetPrivateDataRangeScanIterator(ns, coll, startKey, endKey)
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  	return &pvtdataResultsItr{ns, coll, dbItr}, nil
   345  }
   346  
   347  // ExecuteQueryOnPrivateData implements method in interface `ledger.QueryExecutor`
   348  func (q *queryExecutor) ExecuteQueryOnPrivateData(ns, coll, query string) (commonledger.ResultsIterator, error) {
   349  	if err := q.validateCollName(ns, coll); err != nil {
   350  		return nil, err
   351  	}
   352  	if err := q.checkDone(); err != nil {
   353  		return nil, err
   354  	}
   355  	dbItr, err := q.txmgr.db.ExecuteQueryOnPrivateData(ns, coll, query)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  	return &pvtdataResultsItr{ns, coll, dbItr}, nil
   360  }
   361  
   362  // Done implements method in interface `ledger.QueryExecutor`
   363  func (q *queryExecutor) Done() {
   364  	logger.Debugf("Done with transaction simulation / query execution [%s]", q.txid)
   365  	if q.doneInvoked {
   366  		return
   367  	}
   368  
   369  	defer func() {
   370  		q.txmgr.commitRWLock.RUnlock()
   371  		q.doneInvoked = true
   372  		for _, itr := range q.itrs {
   373  			itr.Close()
   374  		}
   375  	}()
   376  }
   377  
   378  func (q *queryExecutor) checkDone() error {
   379  	if q.doneInvoked {
   380  		return errors.New("this instance should not be used after calling Done()")
   381  	}
   382  	return nil
   383  }
   384  
   385  func (q *queryExecutor) validateCollName(ns, coll string) error {
   386  	return q.collNameValidator.validateCollName(ns, coll)
   387  }
   388  
   389  // resultsItr implements interface ledger.ResultsIterator
   390  // this wraps the actual db iterator and intercept the calls
   391  // to build rangeQueryInfo in the ReadWriteSet that is used
   392  // for performing phantom read validation during commit
   393  type resultsItr struct {
   394  	ns                      string
   395  	endKey                  string
   396  	dbItr                   statedb.ResultsIterator
   397  	rwSetBuilder            *rwsetutil.RWSetBuilder
   398  	rangeQueryInfo          *kvrwset.RangeQueryInfo
   399  	rangeQueryResultsHelper *rwsetutil.RangeQueryResultsHelper
   400  }
   401  
   402  func newResultsItr(ns string, startKey string, endKey string, pageSize int32,
   403  	db statedb.VersionedDB, rwsetBuilder *rwsetutil.RWSetBuilder, enableHashing bool,
   404  	maxDegree uint32, hashFunc rwsetutil.HashFunc) (*resultsItr, error) {
   405  	var err error
   406  	var dbItr statedb.ResultsIterator
   407  	if pageSize == 0 {
   408  		dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey)
   409  	} else {
   410  		dbItr, err = db.GetStateRangeScanIteratorWithPagination(ns, startKey, endKey, pageSize)
   411  	}
   412  	if err != nil {
   413  		return nil, err
   414  	}
   415  	itr := &resultsItr{ns: ns, dbItr: dbItr}
   416  	// it's a simulation request so, enable capture of range query info
   417  	if rwsetBuilder != nil {
   418  		itr.rwSetBuilder = rwsetBuilder
   419  		itr.endKey = endKey
   420  		// just set the StartKey... set the EndKey later below in the Next() method.
   421  		itr.rangeQueryInfo = &kvrwset.RangeQueryInfo{StartKey: startKey}
   422  		resultsHelper, err := rwsetutil.NewRangeQueryResultsHelper(enableHashing, maxDegree, hashFunc)
   423  		if err != nil {
   424  			return nil, err
   425  		}
   426  		itr.rangeQueryResultsHelper = resultsHelper
   427  	}
   428  	return itr, nil
   429  }
   430  
   431  // Next implements method in interface ledger.ResultsIterator
   432  // Before returning the next result, update the EndKey and ItrExhausted in rangeQueryInfo
   433  // If we set the EndKey in the constructor (as we do for the StartKey) to what is
   434  // supplied in the original query, we may be capturing the unnecessary longer range if the
   435  // caller decides to stop iterating at some intermediate point. Alternatively, we could have
   436  // set the EndKey and ItrExhausted in the Close() function but it may not be desirable to change
   437  // transactional behaviour based on whether the Close() was invoked or not
   438  func (itr *resultsItr) Next() (commonledger.QueryResult, error) {
   439  	queryResult, err := itr.dbItr.Next()
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  	if err := itr.updateRangeQueryInfo(queryResult); err != nil {
   444  		return nil, err
   445  	}
   446  	if queryResult == nil {
   447  		return nil, nil
   448  	}
   449  
   450  	return &queryresult.KV{
   451  		Namespace: queryResult.Namespace,
   452  		Key:       queryResult.Key,
   453  		Value:     queryResult.Value,
   454  	}, nil
   455  }
   456  
   457  // GetBookmarkAndClose implements method in interface ledger.ResultsIterator
   458  func (itr *resultsItr) GetBookmarkAndClose() string {
   459  	returnBookmark := ""
   460  	if queryResultIterator, ok := itr.dbItr.(statedb.QueryResultsIterator); ok {
   461  		returnBookmark = queryResultIterator.GetBookmarkAndClose()
   462  	}
   463  	return returnBookmark
   464  }
   465  
   466  // updateRangeQueryInfo updates two attributes of the rangeQueryInfo
   467  // 1) The EndKey - set to either a) latest key that is to be returned to the caller (if the iterator is not exhausted)
   468  //                                  because, we do not know if the caller is again going to invoke Next() or not.
   469  //                            or b) the last key that was supplied in the original query (if the iterator is exhausted)
   470  // 2) The ItrExhausted - set to true if the iterator is going to return nil as a result of the Next() call
   471  func (itr *resultsItr) updateRangeQueryInfo(queryResult *statedb.VersionedKV) error {
   472  	if itr.rwSetBuilder == nil {
   473  		return nil
   474  	}
   475  
   476  	if queryResult == nil {
   477  		// caller scanned till the iterator got exhausted.
   478  		// So, set the endKey to the actual endKey supplied in the query
   479  		itr.rangeQueryInfo.ItrExhausted = true
   480  		itr.rangeQueryInfo.EndKey = itr.endKey
   481  		return nil
   482  	}
   483  
   484  	if err := itr.rangeQueryResultsHelper.AddResult(rwsetutil.NewKVRead(queryResult.Key, queryResult.Version)); err != nil {
   485  		return err
   486  	}
   487  	// Set the end key to the latest key retrieved by the caller.
   488  	// Because, the caller may actually not invoke the Next() function again
   489  	itr.rangeQueryInfo.EndKey = queryResult.Key
   490  	return nil
   491  }
   492  
   493  // Close implements method in interface ledger.ResultsIterator
   494  func (itr *resultsItr) Close() {
   495  	itr.dbItr.Close()
   496  }
   497  
   498  type queryResultsItr struct {
   499  	DBItr        statedb.ResultsIterator
   500  	RWSetBuilder *rwsetutil.RWSetBuilder
   501  }
   502  
   503  // Next implements method in interface ledger.ResultsIterator
   504  func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) {
   505  	queryResult, err := itr.DBItr.Next()
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	if queryResult == nil {
   510  		return nil, nil
   511  	}
   512  	logger.Debugf("queryResultsItr.Next() returned a record:%s", string(queryResult.Value))
   513  
   514  	if itr.RWSetBuilder != nil {
   515  		itr.RWSetBuilder.AddToReadSet(queryResult.Namespace, queryResult.Key, queryResult.Version)
   516  	}
   517  	return &queryresult.KV{
   518  		Namespace: queryResult.Namespace,
   519  		Key:       queryResult.Key,
   520  		Value:     queryResult.Value,
   521  	}, nil
   522  }
   523  
   524  // Close implements method in interface ledger.ResultsIterator
   525  func (itr *queryResultsItr) Close() {
   526  	itr.DBItr.Close()
   527  }
   528  
   529  func (itr *queryResultsItr) GetBookmarkAndClose() string {
   530  	returnBookmark := ""
   531  	if queryResultIterator, ok := itr.DBItr.(statedb.QueryResultsIterator); ok {
   532  		returnBookmark = queryResultIterator.GetBookmarkAndClose()
   533  	}
   534  	return returnBookmark
   535  }
   536  
   537  func decomposeVersionedValue(versionedValue *statedb.VersionedValue) ([]byte, []byte, *version.Height) {
   538  	var value []byte
   539  	var metadata []byte
   540  	var ver *version.Height
   541  	if versionedValue != nil {
   542  		value = versionedValue.Value
   543  		ver = versionedValue.Version
   544  		metadata = versionedValue.Metadata
   545  	}
   546  	return value, metadata, ver
   547  }
   548  
   549  // pvtdataResultsItr iterates over results of a query on pvt data
   550  type pvtdataResultsItr struct {
   551  	ns    string
   552  	coll  string
   553  	dbItr statedb.ResultsIterator
   554  }
   555  
   556  // Next implements method in interface ledger.ResultsIterator
   557  func (itr *pvtdataResultsItr) Next() (commonledger.QueryResult, error) {
   558  	queryResult, err := itr.dbItr.Next()
   559  	if err != nil {
   560  		return nil, err
   561  	}
   562  	if queryResult == nil {
   563  		return nil, nil
   564  	}
   565  	return &queryresult.KV{
   566  		Namespace: itr.ns,
   567  		Key:       queryResult.Key,
   568  		Value:     queryResult.Value,
   569  	}, nil
   570  }
   571  
   572  // Close implements method in interface ledger.ResultsIterator
   573  func (itr *pvtdataResultsItr) Close() {
   574  	itr.dbItr.Close()
   575  }
   576  
   577  func (q *queryExecutor) addRangeQueryInfo() {
   578  	if !q.collectReadset {
   579  		return
   580  	}
   581  	for _, itr := range q.itrs {
   582  		results, hash, err := itr.rangeQueryResultsHelper.Done()
   583  		if err != nil {
   584  			q.err = err
   585  			return
   586  		}
   587  		if results != nil {
   588  			rwsetutil.SetRawReads(itr.rangeQueryInfo, results)
   589  		}
   590  		if hash != nil {
   591  			rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash)
   592  		}
   593  		q.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo)
   594  	}
   595  }