github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/cursor.go (about)

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package mongo
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"reflect"
    15  
    16  	"go.mongodb.org/mongo-driver/bson"
    17  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
    18  	"go.mongodb.org/mongo-driver/bson/bsonrw"
    19  	"go.mongodb.org/mongo-driver/mongo/options"
    20  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    21  	"go.mongodb.org/mongo-driver/x/mongo/driver"
    22  	"go.mongodb.org/mongo-driver/x/mongo/driver/session"
    23  )
    24  
    25  // Cursor is used to iterate over a stream of documents. Each document can be decoded into a Go type via the Decode
    26  // method or accessed as raw BSON via the Current field. This type is not goroutine safe and must not be used
    27  // concurrently by multiple goroutines.
    28  type Cursor struct {
    29  	// Current contains the BSON bytes of the current change document. This property is only valid until the next call
    30  	// to Next or TryNext. If continued access is required, a copy must be made.
    31  	Current bson.Raw
    32  
    33  	bc            batchCursor
    34  	batch         *bsoncore.DocumentSequence
    35  	batchLength   int
    36  	bsonOpts      *options.BSONOptions
    37  	registry      *bsoncodec.Registry
    38  	clientSession *session.Client
    39  
    40  	err error
    41  }
    42  
    43  func newCursor(
    44  	bc batchCursor,
    45  	bsonOpts *options.BSONOptions,
    46  	registry *bsoncodec.Registry,
    47  ) (*Cursor, error) {
    48  	return newCursorWithSession(bc, bsonOpts, registry, nil)
    49  }
    50  
    51  func newCursorWithSession(
    52  	bc batchCursor,
    53  	bsonOpts *options.BSONOptions,
    54  	registry *bsoncodec.Registry,
    55  	clientSession *session.Client,
    56  ) (*Cursor, error) {
    57  	if registry == nil {
    58  		registry = bson.DefaultRegistry
    59  	}
    60  	if bc == nil {
    61  		return nil, errors.New("batch cursor must not be nil")
    62  	}
    63  	c := &Cursor{
    64  		bc:            bc,
    65  		bsonOpts:      bsonOpts,
    66  		registry:      registry,
    67  		clientSession: clientSession,
    68  	}
    69  	if bc.ID() == 0 {
    70  		c.closeImplicitSession()
    71  	}
    72  
    73  	// Initialize just the batchLength here so RemainingBatchLength will return an accurate result. The actual batch
    74  	// will be pulled up by the first Next/TryNext call.
    75  	c.batchLength = c.bc.Batch().DocumentCount()
    76  	return c, nil
    77  }
    78  
    79  func newEmptyCursor() *Cursor {
    80  	return &Cursor{bc: driver.NewEmptyBatchCursor()}
    81  }
    82  
    83  // NewCursorFromDocuments creates a new Cursor pre-loaded with the provided documents, error and registry. If no registry is provided,
    84  // bson.DefaultRegistry will be used.
    85  //
    86  // The documents parameter must be a slice of documents. The slice may be nil or empty, but all elements must be non-nil.
    87  func NewCursorFromDocuments(documents []interface{}, err error, registry *bsoncodec.Registry) (*Cursor, error) {
    88  	if registry == nil {
    89  		registry = bson.DefaultRegistry
    90  	}
    91  
    92  	// Convert documents slice to a sequence-style byte array.
    93  	var docsBytes []byte
    94  	for _, doc := range documents {
    95  		switch t := doc.(type) {
    96  		case nil:
    97  			return nil, ErrNilDocument
    98  		case []byte:
    99  			// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.
   100  			doc = bson.Raw(t)
   101  		}
   102  		var marshalErr error
   103  		docsBytes, marshalErr = bson.MarshalAppendWithRegistry(registry, docsBytes, doc)
   104  		if marshalErr != nil {
   105  			return nil, marshalErr
   106  		}
   107  	}
   108  
   109  	c := &Cursor{
   110  		bc:       driver.NewBatchCursorFromDocuments(docsBytes),
   111  		registry: registry,
   112  		err:      err,
   113  	}
   114  
   115  	// Initialize batch and batchLength here. The underlying batch cursor will be preloaded with the
   116  	// provided contents, and thus already has a batch before calls to Next/TryNext.
   117  	c.batch = c.bc.Batch()
   118  	c.batchLength = c.bc.Batch().DocumentCount()
   119  	return c, nil
   120  }
   121  
   122  // ID returns the ID of this cursor, or 0 if the cursor has been closed or exhausted.
   123  func (c *Cursor) ID() int64 { return c.bc.ID() }
   124  
   125  // Next gets the next document for this cursor. It returns true if there were no errors and the cursor has not been
   126  // exhausted.
   127  //
   128  // Next blocks until a document is available or an error occurs. If the context expires, the cursor's error will
   129  // be set to ctx.Err(). In case of an error, Next will return false.
   130  //
   131  // If Next returns false, subsequent calls will also return false.
   132  func (c *Cursor) Next(ctx context.Context) bool {
   133  	return c.next(ctx, false)
   134  }
   135  
   136  // TryNext attempts to get the next document for this cursor. It returns true if there were no errors and the next
   137  // document is available. This is only recommended for use with tailable cursors as a non-blocking alternative to
   138  // Next. See https://www.mongodb.com/docs/manual/core/tailable-cursors/ for more information about tailable cursors.
   139  //
   140  // TryNext returns false if the cursor is exhausted, an error occurs when getting results from the server, the next
   141  // document is not yet available, or ctx expires. If the context  expires, the cursor's error will be set to ctx.Err().
   142  //
   143  // If TryNext returns false and an error occurred or the cursor has been exhausted (i.e. c.Err() != nil || c.ID() == 0),
   144  // subsequent attempts will also return false. Otherwise, it is safe to call TryNext again until a document is
   145  // available.
   146  //
   147  // This method requires driver version >= 1.2.0.
   148  func (c *Cursor) TryNext(ctx context.Context) bool {
   149  	return c.next(ctx, true)
   150  }
   151  
   152  func (c *Cursor) next(ctx context.Context, nonBlocking bool) bool {
   153  	// return false right away if the cursor has already errored.
   154  	if c.err != nil {
   155  		return false
   156  	}
   157  
   158  	if ctx == nil {
   159  		ctx = context.Background()
   160  	}
   161  	doc, err := c.batch.Next()
   162  	switch err {
   163  	case nil:
   164  		// Consume the next document in the current batch.
   165  		c.batchLength--
   166  		c.Current = bson.Raw(doc)
   167  		return true
   168  	case io.EOF: // Need to do a getMore
   169  	default:
   170  		c.err = err
   171  		return false
   172  	}
   173  
   174  	// call the Next method in a loop until at least one document is returned in the next batch or
   175  	// the context times out.
   176  	for {
   177  		// If we don't have a next batch
   178  		if !c.bc.Next(ctx) {
   179  			// Do we have an error? If so we return false.
   180  			c.err = replaceErrors(c.bc.Err())
   181  			if c.err != nil {
   182  				return false
   183  			}
   184  			// Is the cursor ID zero?
   185  			if c.bc.ID() == 0 {
   186  				c.closeImplicitSession()
   187  				return false
   188  			}
   189  			// empty batch, but cursor is still valid.
   190  			// use nonBlocking to determine if we should continue or return control to the caller.
   191  			if nonBlocking {
   192  				return false
   193  			}
   194  			continue
   195  		}
   196  
   197  		// close the implicit session if this was the last getMore
   198  		if c.bc.ID() == 0 {
   199  			c.closeImplicitSession()
   200  		}
   201  
   202  		// Use the new batch to update the batch and batchLength fields. Consume the first document in the batch.
   203  		c.batch = c.bc.Batch()
   204  		c.batchLength = c.batch.DocumentCount()
   205  		doc, err = c.batch.Next()
   206  		switch err {
   207  		case nil:
   208  			c.batchLength--
   209  			c.Current = bson.Raw(doc)
   210  			return true
   211  		case io.EOF: // Empty batch so we continue
   212  		default:
   213  			c.err = err
   214  			return false
   215  		}
   216  	}
   217  }
   218  
   219  func getDecoder(
   220  	data []byte,
   221  	opts *options.BSONOptions,
   222  	reg *bsoncodec.Registry,
   223  ) (*bson.Decoder, error) {
   224  	dec, err := bson.NewDecoder(bsonrw.NewBSONDocumentReader(data))
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	if opts != nil {
   230  		if opts.AllowTruncatingDoubles {
   231  			dec.AllowTruncatingDoubles()
   232  		}
   233  		if opts.BinaryAsSlice {
   234  			dec.BinaryAsSlice()
   235  		}
   236  		if opts.DefaultDocumentD {
   237  			dec.DefaultDocumentD()
   238  		}
   239  		if opts.DefaultDocumentM {
   240  			dec.DefaultDocumentM()
   241  		}
   242  		if opts.UseJSONStructTags {
   243  			dec.UseJSONStructTags()
   244  		}
   245  		if opts.UseLocalTimeZone {
   246  			dec.UseLocalTimeZone()
   247  		}
   248  		if opts.ZeroMaps {
   249  			dec.ZeroMaps()
   250  		}
   251  		if opts.ZeroStructs {
   252  			dec.ZeroStructs()
   253  		}
   254  	}
   255  
   256  	if reg != nil {
   257  		// TODO:(GODRIVER-2719): Remove error handling.
   258  		if err := dec.SetRegistry(reg); err != nil {
   259  			return nil, err
   260  		}
   261  	}
   262  
   263  	return dec, nil
   264  }
   265  
   266  // Decode will unmarshal the current document into val and return any errors from the unmarshalling process without any
   267  // modification. If val is nil or is a typed nil, an error will be returned.
   268  func (c *Cursor) Decode(val interface{}) error {
   269  	dec, err := getDecoder(c.Current, c.bsonOpts, c.registry)
   270  	if err != nil {
   271  		return fmt.Errorf("error configuring BSON decoder: %w", err)
   272  	}
   273  
   274  	return dec.Decode(val)
   275  }
   276  
   277  // Err returns the last error seen by the Cursor, or nil if no error has occurred.
   278  func (c *Cursor) Err() error { return c.err }
   279  
   280  // Close closes this cursor. Next and TryNext must not be called after Close has been called. Close is idempotent. After
   281  // the first call, any subsequent calls will not change the state.
   282  func (c *Cursor) Close(ctx context.Context) error {
   283  	defer c.closeImplicitSession()
   284  	return replaceErrors(c.bc.Close(ctx))
   285  }
   286  
   287  // All iterates the cursor and decodes each document into results. The results parameter must be a pointer to a slice.
   288  // The slice pointed to by results will be completely overwritten. This method will close the cursor after retrieving
   289  // all documents. If the cursor has been iterated, any previously iterated documents will not be included in results.
   290  //
   291  // This method requires driver version >= 1.1.0.
   292  func (c *Cursor) All(ctx context.Context, results interface{}) error {
   293  	resultsVal := reflect.ValueOf(results)
   294  	if resultsVal.Kind() != reflect.Ptr {
   295  		return fmt.Errorf("results argument must be a pointer to a slice, but was a %s", resultsVal.Kind())
   296  	}
   297  
   298  	sliceVal := resultsVal.Elem()
   299  	if sliceVal.Kind() == reflect.Interface {
   300  		sliceVal = sliceVal.Elem()
   301  	}
   302  
   303  	if sliceVal.Kind() != reflect.Slice {
   304  		return fmt.Errorf("results argument must be a pointer to a slice, but was a pointer to %s", sliceVal.Kind())
   305  	}
   306  
   307  	elementType := sliceVal.Type().Elem()
   308  	var index int
   309  	var err error
   310  
   311  	// Defer a call to Close to try to clean up the cursor server-side when all
   312  	// documents have not been exhausted. Use context.Background() to ensure Close
   313  	// completes even if the context passed to All has errored.
   314  	defer c.Close(context.Background())
   315  
   316  	batch := c.batch // exhaust the current batch before iterating the batch cursor
   317  	for {
   318  		sliceVal, index, err = c.addFromBatch(sliceVal, elementType, batch, index)
   319  		if err != nil {
   320  			return err
   321  		}
   322  
   323  		if !c.bc.Next(ctx) {
   324  			break
   325  		}
   326  
   327  		batch = c.bc.Batch()
   328  	}
   329  
   330  	if err = replaceErrors(c.bc.Err()); err != nil {
   331  		return err
   332  	}
   333  
   334  	resultsVal.Elem().Set(sliceVal.Slice(0, index))
   335  	return nil
   336  }
   337  
   338  // RemainingBatchLength returns the number of documents left in the current batch. If this returns zero, the subsequent
   339  // call to Next or TryNext will do a network request to fetch the next batch.
   340  func (c *Cursor) RemainingBatchLength() int {
   341  	return c.batchLength
   342  }
   343  
   344  // addFromBatch adds all documents from batch to sliceVal starting at the given index. It returns the new slice value,
   345  // the next empty index in the slice, and an error if one occurs.
   346  func (c *Cursor) addFromBatch(sliceVal reflect.Value, elemType reflect.Type, batch *bsoncore.DocumentSequence,
   347  	index int) (reflect.Value, int, error) {
   348  
   349  	docs, err := batch.Documents()
   350  	if err != nil {
   351  		return sliceVal, index, err
   352  	}
   353  
   354  	for _, doc := range docs {
   355  		if sliceVal.Len() == index {
   356  			// slice is full
   357  			newElem := reflect.New(elemType)
   358  			sliceVal = reflect.Append(sliceVal, newElem.Elem())
   359  			sliceVal = sliceVal.Slice(0, sliceVal.Cap())
   360  		}
   361  
   362  		currElem := sliceVal.Index(index).Addr().Interface()
   363  		dec, err := getDecoder(doc, c.bsonOpts, c.registry)
   364  		if err != nil {
   365  			return sliceVal, index, fmt.Errorf("error configuring BSON decoder: %w", err)
   366  		}
   367  		err = dec.Decode(currElem)
   368  		if err != nil {
   369  			return sliceVal, index, err
   370  		}
   371  
   372  		index++
   373  	}
   374  
   375  	return sliceVal, index, nil
   376  }
   377  
   378  func (c *Cursor) closeImplicitSession() {
   379  	if c.clientSession != nil && c.clientSession.IsImplicit {
   380  		c.clientSession.EndSession()
   381  	}
   382  }
   383  
   384  // SetBatchSize sets the number of documents to fetch from the database with
   385  // each iteration of the cursor's "Next" method. Note that some operations set
   386  // an initial cursor batch size, so this setting only affects subsequent
   387  // document batches fetched from the database.
   388  func (c *Cursor) SetBatchSize(batchSize int32) {
   389  	c.bc.SetBatchSize(batchSize)
   390  }
   391  
   392  // BatchCursorFromCursor returns a driver.BatchCursor for the given Cursor. If there is no underlying
   393  // driver.BatchCursor, nil is returned.
   394  //
   395  // Deprecated: This is an unstable function because the driver.BatchCursor type exists in the "x" package. Neither this
   396  // function nor the driver.BatchCursor type should be used by applications and may be changed or removed in any release.
   397  func BatchCursorFromCursor(c *Cursor) *driver.BatchCursor {
   398  	bc, _ := c.bc.(*driver.BatchCursor)
   399  	return bc
   400  }