github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/txmgr/query_executor.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package txmgr
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
    13  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    14  	commonledger "github.com/osdi23p228/fabric/common/ledger"
    15  	"github.com/osdi23p228/fabric/core/ledger"
    16  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    17  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    18  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb"
    19  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statemetadata"
    20  	"github.com/osdi23p228/fabric/core/ledger/util"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  const (
    25  	queryReadsHashingEnabled   = true
    26  	maxDegreeQueryReadsHashing = uint32(50)
    27  )
    28  
    29  // queryExecutor is a query executor used in `LockBasedTxMgr`
    30  type queryExecutor struct {
    31  	txmgr             *LockBasedTxMgr
    32  	collNameValidator *collNameValidator
    33  	collectReadset    bool
    34  	rwsetBuilder      *rwsetutil.RWSetBuilder
    35  	itrs              []*resultsItr
    36  	err               error
    37  	doneInvoked       bool
    38  	hasher            rwsetutil.HashFunc
    39  	txid              string
    40  }
    41  
    42  func newQueryExecutor(txmgr *LockBasedTxMgr,
    43  	txid string,
    44  	rwsetBuilder *rwsetutil.RWSetBuilder,
    45  	performCollCheck bool,
    46  	hashFunc rwsetutil.HashFunc) *queryExecutor {
    47  	logger.Debugf("constructing new query executor txid = [%s]", txid)
    48  	qe := &queryExecutor{}
    49  	qe.txid = txid
    50  	qe.txmgr = txmgr
    51  	if rwsetBuilder != nil {
    52  		qe.collectReadset = true
    53  		qe.rwsetBuilder = rwsetBuilder
    54  	}
    55  	qe.hasher = hashFunc
    56  	validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, qe, !performCollCheck)
    57  	qe.collNameValidator = validator
    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, &ErrPvtdataNotAvailable{Msg: fmt.Sprintf(
   218  			"private data matching public hash version is not available. Public hash version = %s, Private data version = %s",
   219  			hashVersion, ver)}
   220  	}
   221  	if q.collectReadset {
   222  		q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   223  	}
   224  	return val, nil
   225  }
   226  
   227  func (q *queryExecutor) GetPrivateDataHash(ns, coll, key string) ([]byte, error) {
   228  	if err := q.validateCollName(ns, coll); err != nil {
   229  		return nil, err
   230  	}
   231  	if err := q.checkDone(); err != nil {
   232  		return nil, err
   233  	}
   234  	var versionedValue *statedb.VersionedValue
   235  	var err error
   236  	if versionedValue, err = q.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil {
   237  		return nil, err
   238  	}
   239  	valHash, _, ver := decomposeVersionedValue(versionedValue)
   240  	if q.collectReadset {
   241  		q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   242  	}
   243  	return valHash, nil
   244  }
   245  
   246  // GetPrivateDataMetadata implements method in interface `ledger.QueryExecutor`
   247  func (q *queryExecutor) GetPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) {
   248  	if !q.collectReadset {
   249  		// reads versions are not getting recorded, retrieve metadata value via optimized path
   250  		return q.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key))
   251  	}
   252  	if err := q.validateCollName(ns, coll); err != nil {
   253  		return nil, err
   254  	}
   255  	if err := q.checkDone(); err != nil {
   256  		return nil, err
   257  	}
   258  	_, metadataBytes, err := q.getPrivateDataValueHash(ns, coll, key)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	return statemetadata.Deserialize(metadataBytes)
   263  }
   264  
   265  func (q *queryExecutor) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) {
   266  	if err := q.validateCollName(ns, coll); err != nil {
   267  		return nil, nil, err
   268  	}
   269  	if err := q.checkDone(); err != nil {
   270  		return nil, nil, err
   271  	}
   272  	var versionedValue *statedb.VersionedValue
   273  	if versionedValue, err = q.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil {
   274  		return nil, nil, err
   275  	}
   276  	valHash, metadata, ver := decomposeVersionedValue(versionedValue)
   277  	if q.collectReadset {
   278  		q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   279  	}
   280  	return valHash, metadata, nil
   281  }
   282  
   283  // GetPrivateDataMetadataByHash implements method in interface `ledger.QueryExecutor`
   284  func (q *queryExecutor) GetPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) {
   285  	return q.getPrivateDataMetadataByHash(ns, coll, keyhash)
   286  }
   287  
   288  func (q *queryExecutor) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) {
   289  	if err := q.validateCollName(ns, coll); err != nil {
   290  		return nil, err
   291  	}
   292  	if err := q.checkDone(); err != nil {
   293  		return nil, err
   294  	}
   295  	if q.collectReadset {
   296  		// this requires to improve rwset builder to accept a keyhash
   297  		return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet")
   298  	}
   299  	metadataBytes, err := q.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	return statemetadata.Deserialize(metadataBytes)
   304  }
   305  
   306  // GetPrivateDataMultipleKeys implements method in interface `ledger.QueryExecutor`
   307  func (q *queryExecutor) GetPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) {
   308  	if err := q.validateCollName(ns, coll); err != nil {
   309  		return nil, err
   310  	}
   311  	if err := q.checkDone(); err != nil {
   312  		return nil, err
   313  	}
   314  	versionedValues, err := q.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys)
   315  	if err != nil {
   316  		return nil, nil
   317  	}
   318  	values := make([][]byte, len(versionedValues))
   319  	for i, versionedValue := range versionedValues {
   320  		val, _, ver := decomposeVersionedValue(versionedValue)
   321  		if q.collectReadset {
   322  			q.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver)
   323  		}
   324  		values[i] = val
   325  	}
   326  	return values, nil
   327  }
   328  
   329  // GetPrivateDataRangeScanIterator implements method in interface `ledger.QueryExecutor`
   330  func (q *queryExecutor) GetPrivateDataRangeScanIterator(ns, coll, startKey, endKey string) (commonledger.ResultsIterator, error) {
   331  	if err := q.validateCollName(ns, coll); err != nil {
   332  		return nil, err
   333  	}
   334  	if err := q.checkDone(); err != nil {
   335  		return nil, err
   336  	}
   337  	dbItr, err := q.txmgr.db.GetPrivateDataRangeScanIterator(ns, coll, startKey, endKey)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	return &pvtdataResultsItr{ns, coll, dbItr}, nil
   342  }
   343  
   344  // ExecuteQueryOnPrivateData implements method in interface `ledger.QueryExecutor`
   345  func (q *queryExecutor) ExecuteQueryOnPrivateData(ns, coll, query string) (commonledger.ResultsIterator, error) {
   346  	if err := q.validateCollName(ns, coll); err != nil {
   347  		return nil, err
   348  	}
   349  	if err := q.checkDone(); err != nil {
   350  		return nil, err
   351  	}
   352  	dbItr, err := q.txmgr.db.ExecuteQueryOnPrivateData(ns, coll, query)
   353  	if err != nil {
   354  		return nil, err
   355  	}
   356  	return &pvtdataResultsItr{ns, coll, dbItr}, nil
   357  }
   358  
   359  // Done implements method in interface `ledger.QueryExecutor`
   360  func (q *queryExecutor) Done() {
   361  	logger.Debugf("Done with transaction simulation / query execution [%s]", q.txid)
   362  	if q.doneInvoked {
   363  		return
   364  	}
   365  
   366  	defer func() {
   367  		q.txmgr.commitRWLock.RUnlock()
   368  		q.doneInvoked = true
   369  		for _, itr := range q.itrs {
   370  			itr.Close()
   371  		}
   372  	}()
   373  }
   374  
   375  func (q *queryExecutor) checkDone() error {
   376  	if q.doneInvoked {
   377  		return errors.New("this instance should not be used after calling Done()")
   378  	}
   379  	return nil
   380  }
   381  
   382  func (q *queryExecutor) validateCollName(ns, coll string) error {
   383  	return q.collNameValidator.validateCollName(ns, coll)
   384  }
   385  
   386  // resultsItr implements interface ledger.ResultsIterator
   387  // this wraps the actual db iterator and intercept the calls
   388  // to build rangeQueryInfo in the ReadWriteSet that is used
   389  // for performing phantom read validation during commit
   390  type resultsItr struct {
   391  	ns                      string
   392  	endKey                  string
   393  	dbItr                   statedb.ResultsIterator
   394  	rwSetBuilder            *rwsetutil.RWSetBuilder
   395  	rangeQueryInfo          *kvrwset.RangeQueryInfo
   396  	rangeQueryResultsHelper *rwsetutil.RangeQueryResultsHelper
   397  }
   398  
   399  func newResultsItr(ns string, startKey string, endKey string, pageSize int32,
   400  	db statedb.VersionedDB, rwsetBuilder *rwsetutil.RWSetBuilder, enableHashing bool,
   401  	maxDegree uint32, hashFunc rwsetutil.HashFunc) (*resultsItr, error) {
   402  	var err error
   403  	var dbItr statedb.ResultsIterator
   404  	if pageSize == 0 {
   405  		dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey)
   406  	} else {
   407  		dbItr, err = db.GetStateRangeScanIteratorWithPagination(ns, startKey, endKey, pageSize)
   408  	}
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	itr := &resultsItr{ns: ns, dbItr: dbItr}
   413  	// it's a simulation request so, enable capture of range query info
   414  	if rwsetBuilder != nil {
   415  		itr.rwSetBuilder = rwsetBuilder
   416  		itr.endKey = endKey
   417  		// just set the StartKey... set the EndKey later below in the Next() method.
   418  		itr.rangeQueryInfo = &kvrwset.RangeQueryInfo{StartKey: startKey}
   419  		resultsHelper, err := rwsetutil.NewRangeQueryResultsHelper(enableHashing, maxDegree, hashFunc)
   420  		if err != nil {
   421  			return nil, err
   422  		}
   423  		itr.rangeQueryResultsHelper = resultsHelper
   424  	}
   425  	return itr, nil
   426  }
   427  
   428  // Next implements method in interface ledger.ResultsIterator
   429  // Before returning the next result, update the EndKey and ItrExhausted in rangeQueryInfo
   430  // If we set the EndKey in the constructor (as we do for the StartKey) to what is
   431  // supplied in the original query, we may be capturing the unnecessary longer range if the
   432  // caller decides to stop iterating at some intermediate point. Alternatively, we could have
   433  // set the EndKey and ItrExhausted in the Close() function but it may not be desirable to change
   434  // transactional behaviour based on whether the Close() was invoked or not
   435  func (itr *resultsItr) Next() (commonledger.QueryResult, error) {
   436  	queryResult, err := itr.dbItr.Next()
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  	itr.updateRangeQueryInfo(queryResult)
   441  	if queryResult == nil {
   442  		return nil, nil
   443  	}
   444  	versionedKV := queryResult.(*statedb.VersionedKV)
   445  	return &queryresult.KV{Namespace: versionedKV.Namespace, Key: versionedKV.Key, Value: versionedKV.Value}, nil
   446  }
   447  
   448  // GetBookmarkAndClose implements method in interface ledger.ResultsIterator
   449  func (itr *resultsItr) GetBookmarkAndClose() string {
   450  	returnBookmark := ""
   451  	if queryResultIterator, ok := itr.dbItr.(statedb.QueryResultsIterator); ok {
   452  		returnBookmark = queryResultIterator.GetBookmarkAndClose()
   453  	}
   454  	return returnBookmark
   455  }
   456  
   457  // updateRangeQueryInfo updates two attributes of the rangeQueryInfo
   458  // 1) The EndKey - set to either a) latest key that is to be returned to the caller (if the iterator is not exhausted)
   459  //                                  because, we do not know if the caller is again going to invoke Next() or not.
   460  //                            or b) the last key that was supplied in the original query (if the iterator is exhausted)
   461  // 2) The ItrExhausted - set to true if the iterator is going to return nil as a result of the Next() call
   462  func (itr *resultsItr) updateRangeQueryInfo(queryResult statedb.QueryResult) {
   463  	if itr.rwSetBuilder == nil {
   464  		return
   465  	}
   466  
   467  	if queryResult == nil {
   468  		// caller scanned till the iterator got exhausted.
   469  		// So, set the endKey to the actual endKey supplied in the query
   470  		itr.rangeQueryInfo.ItrExhausted = true
   471  		itr.rangeQueryInfo.EndKey = itr.endKey
   472  		return
   473  	}
   474  	versionedKV := queryResult.(*statedb.VersionedKV)
   475  	itr.rangeQueryResultsHelper.AddResult(rwsetutil.NewKVRead(versionedKV.Key, versionedKV.Version))
   476  	// Set the end key to the latest key retrieved by the caller.
   477  	// Because, the caller may actually not invoke the Next() function again
   478  	itr.rangeQueryInfo.EndKey = versionedKV.Key
   479  }
   480  
   481  // Close implements method in interface ledger.ResultsIterator
   482  func (itr *resultsItr) Close() {
   483  	itr.dbItr.Close()
   484  }
   485  
   486  type queryResultsItr struct {
   487  	DBItr        statedb.ResultsIterator
   488  	RWSetBuilder *rwsetutil.RWSetBuilder
   489  }
   490  
   491  // Next implements method in interface ledger.ResultsIterator
   492  func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) {
   493  
   494  	queryResult, err := itr.DBItr.Next()
   495  	if err != nil {
   496  		return nil, err
   497  	}
   498  	if queryResult == nil {
   499  		return nil, nil
   500  	}
   501  	versionedQueryRecord := queryResult.(*statedb.VersionedKV)
   502  	logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Value))
   503  
   504  	if itr.RWSetBuilder != nil {
   505  		itr.RWSetBuilder.AddToReadSet(versionedQueryRecord.Namespace, versionedQueryRecord.Key, versionedQueryRecord.Version)
   506  	}
   507  	return &queryresult.KV{Namespace: versionedQueryRecord.Namespace, Key: versionedQueryRecord.Key, Value: versionedQueryRecord.Value}, nil
   508  }
   509  
   510  // Close implements method in interface ledger.ResultsIterator
   511  func (itr *queryResultsItr) Close() {
   512  	itr.DBItr.Close()
   513  }
   514  
   515  func (itr *queryResultsItr) GetBookmarkAndClose() string {
   516  	returnBookmark := ""
   517  	if queryResultIterator, ok := itr.DBItr.(statedb.QueryResultsIterator); ok {
   518  		returnBookmark = queryResultIterator.GetBookmarkAndClose()
   519  	}
   520  	return returnBookmark
   521  }
   522  
   523  func decomposeVersionedValue(versionedValue *statedb.VersionedValue) ([]byte, []byte, *version.Height) {
   524  	var value []byte
   525  	var metadata []byte
   526  	var ver *version.Height
   527  	if versionedValue != nil {
   528  		value = versionedValue.Value
   529  		ver = versionedValue.Version
   530  		metadata = versionedValue.Metadata
   531  	}
   532  	return value, metadata, ver
   533  }
   534  
   535  // pvtdataResultsItr iterates over results of a query on pvt data
   536  type pvtdataResultsItr struct {
   537  	ns    string
   538  	coll  string
   539  	dbItr statedb.ResultsIterator
   540  }
   541  
   542  // Next implements method in interface ledger.ResultsIterator
   543  func (itr *pvtdataResultsItr) Next() (commonledger.QueryResult, error) {
   544  	queryResult, err := itr.dbItr.Next()
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  	if queryResult == nil {
   549  		return nil, nil
   550  	}
   551  	versionedQueryRecord := queryResult.(*statedb.VersionedKV)
   552  	return &queryresult.KV{
   553  		Namespace: itr.ns,
   554  		Key:       versionedQueryRecord.Key,
   555  		Value:     versionedQueryRecord.Value,
   556  	}, nil
   557  }
   558  
   559  // Close implements method in interface ledger.ResultsIterator
   560  func (itr *pvtdataResultsItr) Close() {
   561  	itr.dbItr.Close()
   562  }
   563  
   564  func (q *queryExecutor) addRangeQueryInfo() {
   565  	if !q.collectReadset {
   566  		return
   567  	}
   568  	for _, itr := range q.itrs {
   569  		results, hash, err := itr.rangeQueryResultsHelper.Done()
   570  		if err != nil {
   571  			q.err = err
   572  			return
   573  		}
   574  		if results != nil {
   575  			rwsetutil.SetRawReads(itr.rangeQueryInfo, results)
   576  		}
   577  		if hash != nil {
   578  			rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash)
   579  		}
   580  		q.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo)
   581  	}
   582  }