github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/table/scanner/result.go (about)

     1  package scanner
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"sync/atomic"
     8  
     9  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    10  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats"
    11  
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    13  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
    14  	"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
    15  	"github.com/ydb-platform/ydb-go-sdk/v3/table/stats"
    16  )
    17  
    18  var errAlreadyClosed = xerrors.Wrap(errors.New("result closed early"))
    19  
    20  type baseResult struct {
    21  	valueScanner
    22  
    23  	nextResultSetCounter atomic.Uint64
    24  	statsMtx             xsync.RWMutex
    25  	stats                *Ydb_TableStats.QueryStats
    26  
    27  	closed atomic.Bool
    28  }
    29  
    30  type streamResult struct {
    31  	baseResult
    32  
    33  	recv  func(ctx context.Context) (*Ydb.ResultSet, *Ydb_TableStats.QueryStats, error)
    34  	close func(error) error
    35  }
    36  
    37  // Err returns error caused Scanner to be broken.
    38  func (r *streamResult) Err() error {
    39  	err := r.valueScanner.Err()
    40  	if err != nil {
    41  		return xerrors.WithStackTrace(err)
    42  	}
    43  
    44  	return nil
    45  }
    46  
    47  type unaryResult struct {
    48  	baseResult
    49  
    50  	sets    []*Ydb.ResultSet
    51  	nextSet int
    52  }
    53  
    54  // Err returns error caused Scanner to be broken.
    55  func (r *unaryResult) Err() error {
    56  	err := r.valueScanner.Err()
    57  	if err != nil {
    58  		return xerrors.WithStackTrace(err)
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  // Close closes the result, preventing further iteration.
    65  func (r *unaryResult) Close() error {
    66  	if r.closed.CompareAndSwap(false, true) {
    67  		return nil
    68  	}
    69  
    70  	return xerrors.WithStackTrace(errAlreadyClosed)
    71  }
    72  
    73  func (r *unaryResult) ResultSetCount() int {
    74  	return len(r.sets)
    75  }
    76  
    77  func (r *baseResult) isClosed() bool {
    78  	return r.closed.Load()
    79  }
    80  
    81  type resultWithError interface {
    82  	SetErr(err error)
    83  }
    84  
    85  type UnaryResult interface {
    86  	result.Result
    87  	resultWithError
    88  }
    89  
    90  type StreamResult interface {
    91  	result.StreamResult
    92  	resultWithError
    93  }
    94  
    95  type option func(r *baseResult)
    96  
    97  func WithIgnoreTruncated(ignoreTruncated bool) option {
    98  	return func(r *baseResult) {
    99  		r.valueScanner.ignoreTruncated = ignoreTruncated
   100  	}
   101  }
   102  
   103  func WithMarkTruncatedAsRetryable() option {
   104  	return func(r *baseResult) {
   105  		r.valueScanner.markTruncatedAsRetryable = true
   106  	}
   107  }
   108  
   109  func NewStream(
   110  	ctx context.Context,
   111  	recv func(ctx context.Context) (*Ydb.ResultSet, *Ydb_TableStats.QueryStats, error),
   112  	onClose func(error) error,
   113  	opts ...option,
   114  ) (StreamResult, error) {
   115  	r := &streamResult{
   116  		recv:  recv,
   117  		close: onClose,
   118  	}
   119  	for _, o := range opts {
   120  		if o != nil {
   121  			o(&r.baseResult)
   122  		}
   123  	}
   124  	if err := r.nextResultSetErr(ctx); err != nil {
   125  		return nil, xerrors.WithStackTrace(err)
   126  	}
   127  
   128  	return r, nil
   129  }
   130  
   131  func NewUnary(sets []*Ydb.ResultSet, stats *Ydb_TableStats.QueryStats, opts ...option) UnaryResult {
   132  	r := &unaryResult{
   133  		baseResult: baseResult{
   134  			stats: stats,
   135  		},
   136  		sets: sets,
   137  	}
   138  	for _, o := range opts {
   139  		if o != nil {
   140  			o(&r.baseResult)
   141  		}
   142  	}
   143  
   144  	return r
   145  }
   146  
   147  func (r *baseResult) Reset(set *Ydb.ResultSet, columnNames ...string) {
   148  	r.reset(set)
   149  	if set != nil {
   150  		r.setColumnIndexes(columnNames)
   151  	}
   152  }
   153  
   154  func (r *unaryResult) NextResultSetErr(ctx context.Context, columns ...string) (err error) {
   155  	if r.isClosed() {
   156  		return xerrors.WithStackTrace(errAlreadyClosed)
   157  	}
   158  	if !r.HasNextResultSet() {
   159  		return io.EOF
   160  	}
   161  	r.Reset(r.sets[r.nextSet], columns...)
   162  	r.nextSet++
   163  
   164  	return ctx.Err()
   165  }
   166  
   167  func (r *unaryResult) NextResultSet(ctx context.Context, columns ...string) bool {
   168  	return r.NextResultSetErr(ctx, columns...) == nil
   169  }
   170  
   171  func (r *streamResult) nextResultSetErr(ctx context.Context, columns ...string) (err error) {
   172  	// skipping second recv because first call of recv is from New Stream(), second call is from user
   173  	if r.nextResultSetCounter.Add(1) == 2 {
   174  		r.setColumnIndexes(columns)
   175  
   176  		return ctx.Err()
   177  	}
   178  	s, stats, err := r.recv(ctx)
   179  	if err != nil {
   180  		r.Reset(nil)
   181  		if xerrors.Is(err, io.EOF) {
   182  			return err
   183  		}
   184  
   185  		return r.errorf(1, "streamResult.NextResultSetErr(): %w", err)
   186  	}
   187  	r.Reset(s, columns...)
   188  	if stats != nil {
   189  		r.statsMtx.WithLock(func() {
   190  			r.stats = stats
   191  		})
   192  	}
   193  
   194  	return ctx.Err()
   195  }
   196  
   197  func (r *streamResult) NextResultSetErr(ctx context.Context, columns ...string) (err error) {
   198  	if r.isClosed() {
   199  		return xerrors.WithStackTrace(errAlreadyClosed)
   200  	}
   201  	if err = r.Err(); err != nil {
   202  		return xerrors.WithStackTrace(err)
   203  	}
   204  	if err := r.nextResultSetErr(ctx, columns...); err != nil {
   205  		if xerrors.Is(err, io.EOF) {
   206  			return io.EOF
   207  		}
   208  
   209  		return xerrors.WithStackTrace(err)
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  func (r *streamResult) NextResultSet(ctx context.Context, columns ...string) bool {
   216  	return r.NextResultSetErr(ctx, columns...) == nil
   217  }
   218  
   219  // CurrentResultSet get current result set
   220  func (r *baseResult) CurrentResultSet() result.Set {
   221  	return r
   222  }
   223  
   224  // Stats returns query execution queryStats.
   225  func (r *baseResult) Stats() stats.QueryStats {
   226  	var s queryStats
   227  	r.statsMtx.WithRLock(func() {
   228  		s.stats = r.stats
   229  	})
   230  
   231  	if s.stats == nil {
   232  		return nil
   233  	}
   234  
   235  	return &s
   236  }
   237  
   238  // Close closes the result, preventing further iteration.
   239  func (r *streamResult) Close() (err error) {
   240  	if r.closed.CompareAndSwap(false, true) {
   241  		return r.close(r.Err())
   242  	}
   243  
   244  	return xerrors.WithStackTrace(errAlreadyClosed)
   245  }
   246  
   247  func (r *baseResult) inactive() bool {
   248  	return r.isClosed() || r.Err() != nil
   249  }
   250  
   251  // HasNextResultSet reports whether result set may be advanced.
   252  // It may be useful to call HasNextResultSet() instead of NextResultSet() to look ahead
   253  // without advancing the result set.
   254  // Note that it does not work with sets from stream.
   255  func (r *streamResult) HasNextResultSet() bool {
   256  	return !r.inactive()
   257  }
   258  
   259  // HasNextResultSet reports whether result set may be advanced.
   260  // It may be useful to call HasNextResultSet() instead of NextResultSet() to look ahead
   261  // without advancing the result set.
   262  // Note that it does not work with sets from stream.
   263  func (r *unaryResult) HasNextResultSet() bool {
   264  	if r.inactive() || r.nextSet >= len(r.sets) {
   265  		return false
   266  	}
   267  
   268  	return true
   269  }