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