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

     1  package table
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"strconv"
     8  	"sync"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/ydb-platform/ydb-go-genproto/Ydb_Table_V1"
    13  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    14  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Table"
    15  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/metadata"
    18  
    19  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
    20  	balancerContext "github.com/ydb-platform/ydb-go-sdk/v3/internal/balancer"
    21  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/conn"
    22  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/feature"
    23  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/meta"
    24  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
    25  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
    26  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
    27  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
    28  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner"
    29  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
    30  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
    31  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
    32  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    33  	"github.com/ydb-platform/ydb-go-sdk/v3/retry"
    34  	"github.com/ydb-platform/ydb-go-sdk/v3/table"
    35  	"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
    36  	"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
    37  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    38  )
    39  
    40  // session represents a single table API session.
    41  //
    42  // session methods are not goroutine safe. Simultaneous execution of requests
    43  // are forbidden within a single session.
    44  //
    45  // Note that after session is no longer needed it should be destroyed by
    46  // Close() call.
    47  type session struct {
    48  	onClose      []func(s *session)
    49  	id           string
    50  	tableService Ydb_Table_V1.TableServiceClient
    51  	status       table.SessionStatus
    52  	config       *config.Config
    53  	lastUsage    atomic.Int64
    54  	statusMtx    sync.RWMutex
    55  	closeOnce    sync.Once
    56  	nodeID       atomic.Uint32
    57  }
    58  
    59  func (s *session) LastUsage() time.Time {
    60  	return time.Unix(s.lastUsage.Load(), 0)
    61  }
    62  
    63  func nodeID(sessionID string) (uint32, error) {
    64  	u, err := url.Parse(sessionID)
    65  	if err != nil {
    66  		return 0, err
    67  	}
    68  	id, err := strconv.ParseUint(u.Query().Get("node_id"), 10, 32)
    69  	if err != nil {
    70  		return 0, err
    71  	}
    72  
    73  	return uint32(id), err
    74  }
    75  
    76  func (s *session) NodeID() uint32 {
    77  	if s == nil {
    78  		return 0
    79  	}
    80  	if id := s.nodeID.Load(); id != 0 {
    81  		return id
    82  	}
    83  	id, err := nodeID(s.id)
    84  	if err != nil {
    85  		return 0
    86  	}
    87  	s.nodeID.Store(id)
    88  
    89  	return id
    90  }
    91  
    92  func (s *session) Status() table.SessionStatus {
    93  	if s == nil {
    94  		return table.SessionStatusUnknown
    95  	}
    96  	s.statusMtx.RLock()
    97  	defer s.statusMtx.RUnlock()
    98  
    99  	return s.status
   100  }
   101  
   102  func (s *session) SetStatus(status table.SessionStatus) {
   103  	s.statusMtx.Lock()
   104  	defer s.statusMtx.Unlock()
   105  	s.status = status
   106  }
   107  
   108  func (s *session) isClosed() bool {
   109  	return s.Status() == table.SessionClosed
   110  }
   111  
   112  func (s *session) isClosing() bool {
   113  	return s.Status() == table.SessionClosing
   114  }
   115  
   116  func newSession(ctx context.Context, cc grpc.ClientConnInterface, config *config.Config) (
   117  	s *session, err error,
   118  ) {
   119  	onDone := trace.TableOnSessionNew(config.Trace(), &ctx, stack.FunctionID(""))
   120  	defer func() {
   121  		onDone(s, err)
   122  	}()
   123  	var (
   124  		response *Ydb_Table.CreateSessionResponse
   125  		result   Ydb_Table.CreateSessionResult
   126  		c        = Ydb_Table_V1.NewTableServiceClient(cc)
   127  	)
   128  	response, err = c.CreateSession(ctx,
   129  		&Ydb_Table.CreateSessionRequest{
   130  			OperationParams: operation.Params(
   131  				ctx,
   132  				config.OperationTimeout(),
   133  				config.OperationCancelAfter(),
   134  				operation.ModeSync,
   135  			),
   136  		},
   137  	)
   138  	if err != nil {
   139  		return nil, xerrors.WithStackTrace(err)
   140  	}
   141  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   142  	if err != nil {
   143  		return nil, xerrors.WithStackTrace(err)
   144  	}
   145  
   146  	s = &session{
   147  		id:     result.GetSessionId(),
   148  		config: config,
   149  		status: table.SessionReady,
   150  	}
   151  	s.lastUsage.Store(time.Now().Unix())
   152  
   153  	s.tableService = Ydb_Table_V1.NewTableServiceClient(
   154  		conn.WithBeforeFunc(
   155  			conn.WithContextModifier(cc, func(ctx context.Context) context.Context {
   156  				return meta.WithTrailerCallback(balancerContext.WithEndpoint(ctx, s), s.checkCloseHint)
   157  			}),
   158  			func() {
   159  				s.lastUsage.Store(time.Now().Unix())
   160  			},
   161  		),
   162  	)
   163  
   164  	return s, nil
   165  }
   166  
   167  func (s *session) ID() string {
   168  	if s == nil {
   169  		return ""
   170  	}
   171  
   172  	return s.id
   173  }
   174  
   175  func (s *session) Close(ctx context.Context) (err error) {
   176  	if s.isClosed() {
   177  		return xerrors.WithStackTrace(errSessionClosed)
   178  	}
   179  
   180  	s.closeOnce.Do(func() {
   181  		onDone := trace.TableOnSessionDelete(s.config.Trace(), &ctx,
   182  			stack.FunctionID(""),
   183  			s,
   184  		)
   185  		defer func() {
   186  			s.SetStatus(table.SessionClosed)
   187  			onDone(err)
   188  		}()
   189  
   190  		if time.Since(s.LastUsage()) < s.config.IdleThreshold() {
   191  			_, err = s.tableService.DeleteSession(ctx,
   192  				&Ydb_Table.DeleteSessionRequest{
   193  					SessionId: s.id,
   194  					OperationParams: operation.Params(ctx,
   195  						s.config.OperationTimeout(),
   196  						s.config.OperationCancelAfter(),
   197  						operation.ModeSync,
   198  					),
   199  				},
   200  			)
   201  		}
   202  
   203  		for _, onClose := range s.onClose {
   204  			onClose(s)
   205  		}
   206  	})
   207  
   208  	if err != nil {
   209  		return xerrors.WithStackTrace(err)
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  func (s *session) checkCloseHint(md metadata.MD) {
   216  	for header, values := range md {
   217  		if header != meta.HeaderServerHints {
   218  			continue
   219  		}
   220  		for _, hint := range values {
   221  			if hint == meta.HintSessionClose {
   222  				s.SetStatus(table.SessionClosing)
   223  			}
   224  		}
   225  	}
   226  }
   227  
   228  // KeepAlive keeps idle session alive.
   229  func (s *session) KeepAlive(ctx context.Context) (err error) {
   230  	var (
   231  		result Ydb_Table.KeepAliveResult
   232  		onDone = trace.TableOnSessionKeepAlive(
   233  			s.config.Trace(), &ctx,
   234  			stack.FunctionID(""),
   235  			s,
   236  		)
   237  	)
   238  	defer func() {
   239  		onDone(err)
   240  	}()
   241  
   242  	resp, err := s.tableService.KeepAlive(ctx,
   243  		&Ydb_Table.KeepAliveRequest{
   244  			SessionId: s.id,
   245  			OperationParams: operation.Params(
   246  				ctx,
   247  				s.config.OperationTimeout(),
   248  				s.config.OperationCancelAfter(),
   249  				operation.ModeSync,
   250  			),
   251  		},
   252  	)
   253  	if err != nil {
   254  		return xerrors.WithStackTrace(err)
   255  	}
   256  
   257  	err = resp.GetOperation().GetResult().UnmarshalTo(&result)
   258  	if err != nil {
   259  		return xerrors.WithStackTrace(err)
   260  	}
   261  
   262  	switch result.GetSessionStatus() {
   263  	case Ydb_Table.KeepAliveResult_SESSION_STATUS_READY:
   264  		s.SetStatus(table.SessionReady)
   265  	case Ydb_Table.KeepAliveResult_SESSION_STATUS_BUSY:
   266  		s.SetStatus(table.SessionBusy)
   267  	}
   268  
   269  	return nil
   270  }
   271  
   272  // CreateTable creates table at given path with given options.
   273  func (s *session) CreateTable(
   274  	ctx context.Context,
   275  	path string,
   276  	opts ...options.CreateTableOption,
   277  ) (err error) {
   278  	var (
   279  		request = Ydb_Table.CreateTableRequest{
   280  			SessionId: s.id,
   281  			Path:      path,
   282  			OperationParams: operation.Params(
   283  				ctx,
   284  				s.config.OperationTimeout(),
   285  				s.config.OperationCancelAfter(),
   286  				operation.ModeSync,
   287  			),
   288  		}
   289  		a = allocator.New()
   290  	)
   291  	defer a.Free()
   292  	for _, opt := range opts {
   293  		if opt != nil {
   294  			opt.ApplyCreateTableOption((*options.CreateTableDesc)(&request), a)
   295  		}
   296  	}
   297  	_, err = s.tableService.CreateTable(ctx, &request)
   298  	if err != nil {
   299  		return xerrors.WithStackTrace(err)
   300  	}
   301  
   302  	return nil
   303  }
   304  
   305  // DescribeTable describes table at given path.
   306  func (s *session) DescribeTable(
   307  	ctx context.Context,
   308  	path string,
   309  	opts ...options.DescribeTableOption,
   310  ) (desc options.Description, err error) {
   311  	var (
   312  		response *Ydb_Table.DescribeTableResponse
   313  		result   Ydb_Table.DescribeTableResult
   314  	)
   315  	request := Ydb_Table.DescribeTableRequest{
   316  		SessionId: s.id,
   317  		Path:      path,
   318  		OperationParams: operation.Params(
   319  			ctx,
   320  			s.config.OperationTimeout(),
   321  			s.config.OperationCancelAfter(),
   322  			operation.ModeSync,
   323  		),
   324  	}
   325  	for _, opt := range opts {
   326  		if opt != nil {
   327  			opt((*options.DescribeTableDesc)(&request))
   328  		}
   329  	}
   330  	response, err = s.tableService.DescribeTable(ctx, &request)
   331  	if err != nil {
   332  		return desc, xerrors.WithStackTrace(err)
   333  	}
   334  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   335  	if err != nil {
   336  		return desc, xerrors.WithStackTrace(err)
   337  	}
   338  
   339  	cs := make(
   340  		[]options.Column,
   341  		len(result.GetColumns()),
   342  	)
   343  	for i, c := range result.GetColumns() {
   344  		cs[i] = options.Column{
   345  			Name:   c.GetName(),
   346  			Type:   types.TypeFromYDB(c.GetType()),
   347  			Family: c.GetFamily(),
   348  		}
   349  	}
   350  
   351  	rs := make(
   352  		[]options.KeyRange,
   353  		len(result.GetShardKeyBounds())+1,
   354  	)
   355  	var last value.Value
   356  	for i, b := range result.GetShardKeyBounds() {
   357  		if last != nil {
   358  			rs[i].From = last
   359  		}
   360  
   361  		bound := value.FromYDB(b.GetType(), b.GetValue())
   362  		rs[i].To = bound
   363  
   364  		last = bound
   365  	}
   366  	if last != nil {
   367  		i := len(rs) - 1
   368  		rs[i].From = last
   369  	}
   370  
   371  	var stats *options.TableStats
   372  	if result.GetTableStats() != nil {
   373  		resStats := result.GetTableStats()
   374  		partStats := make(
   375  			[]options.PartitionStats,
   376  			len(result.GetTableStats().GetPartitionStats()),
   377  		)
   378  		for i, v := range result.GetTableStats().GetPartitionStats() {
   379  			partStats[i].RowsEstimate = v.GetRowsEstimate()
   380  			partStats[i].StoreSize = v.GetStoreSize()
   381  		}
   382  		var creationTime, modificationTime time.Time
   383  		if resStats.GetCreationTime().GetSeconds() != 0 {
   384  			creationTime = time.Unix(
   385  				resStats.GetCreationTime().GetSeconds(),
   386  				int64(resStats.GetCreationTime().GetNanos()),
   387  			)
   388  		}
   389  		if resStats.GetModificationTime().GetSeconds() != 0 {
   390  			modificationTime = time.Unix(
   391  				resStats.GetModificationTime().GetSeconds(),
   392  				int64(resStats.GetModificationTime().GetNanos()),
   393  			)
   394  		}
   395  
   396  		stats = &options.TableStats{
   397  			PartitionStats:   partStats,
   398  			RowsEstimate:     resStats.GetRowsEstimate(),
   399  			StoreSize:        resStats.GetStoreSize(),
   400  			Partitions:       resStats.GetPartitions(),
   401  			CreationTime:     creationTime,
   402  			ModificationTime: modificationTime,
   403  		}
   404  	}
   405  
   406  	cf := make([]options.ColumnFamily, len(result.GetColumnFamilies()))
   407  	for i, c := range result.GetColumnFamilies() {
   408  		cf[i] = options.NewColumnFamily(c)
   409  	}
   410  
   411  	attrs := make(map[string]string, len(result.GetAttributes()))
   412  	for k, v := range result.GetAttributes() {
   413  		attrs[k] = v
   414  	}
   415  
   416  	indexes := make([]options.IndexDescription, len(result.GetIndexes()))
   417  	for i, idx := range result.GetIndexes() {
   418  		var typ options.IndexType
   419  		switch idx.GetType().(type) {
   420  		case *Ydb_Table.TableIndexDescription_GlobalAsyncIndex:
   421  			typ = options.IndexTypeGlobalAsync
   422  		case *Ydb_Table.TableIndexDescription_GlobalIndex:
   423  			typ = options.IndexTypeGlobal
   424  		}
   425  		indexes[i] = options.IndexDescription{
   426  			Name:         idx.GetName(),
   427  			IndexColumns: idx.GetIndexColumns(),
   428  			DataColumns:  idx.GetDataColumns(),
   429  			Status:       idx.GetStatus(),
   430  			Type:         typ,
   431  		}
   432  	}
   433  
   434  	changeFeeds := make([]options.ChangefeedDescription, len(result.GetChangefeeds()))
   435  	for i, proto := range result.GetChangefeeds() {
   436  		changeFeeds[i] = options.NewChangefeedDescription(proto)
   437  	}
   438  
   439  	return options.Description{
   440  		Name:                 result.GetSelf().GetName(),
   441  		PrimaryKey:           result.GetPrimaryKey(),
   442  		Columns:              cs,
   443  		KeyRanges:            rs,
   444  		Stats:                stats,
   445  		ColumnFamilies:       cf,
   446  		Attributes:           attrs,
   447  		ReadReplicaSettings:  options.NewReadReplicasSettings(result.GetReadReplicasSettings()),
   448  		StorageSettings:      options.NewStorageSettings(result.GetStorageSettings()),
   449  		KeyBloomFilter:       feature.FromYDB(result.GetKeyBloomFilter()),
   450  		PartitioningSettings: options.NewPartitioningSettings(result.GetPartitioningSettings()),
   451  		Indexes:              indexes,
   452  		TimeToLiveSettings:   NewTimeToLiveSettings(result.GetTtlSettings()),
   453  		Changefeeds:          changeFeeds,
   454  		Tiering:              result.GetTiering(),
   455  	}, nil
   456  }
   457  
   458  // DropTable drops table at given path with given options.
   459  func (s *session) DropTable(
   460  	ctx context.Context,
   461  	path string,
   462  	opts ...options.DropTableOption,
   463  ) (err error) {
   464  	request := Ydb_Table.DropTableRequest{
   465  		SessionId: s.id,
   466  		Path:      path,
   467  		OperationParams: operation.Params(
   468  			ctx,
   469  			s.config.OperationTimeout(),
   470  			s.config.OperationCancelAfter(),
   471  			operation.ModeSync,
   472  		),
   473  	}
   474  	for _, opt := range opts {
   475  		if opt != nil {
   476  			opt.ApplyDropTableOption((*options.DropTableDesc)(&request))
   477  		}
   478  	}
   479  	_, err = s.tableService.DropTable(ctx, &request)
   480  
   481  	return xerrors.WithStackTrace(err)
   482  }
   483  
   484  func (s *session) checkError(err error) {
   485  	if err == nil {
   486  		return
   487  	}
   488  	if m := retry.Check(err); m.MustDeleteSession() {
   489  		s.SetStatus(table.SessionClosing)
   490  	}
   491  }
   492  
   493  // AlterTable modifies schema of table at given path with given options.
   494  func (s *session) AlterTable(
   495  	ctx context.Context,
   496  	path string,
   497  	opts ...options.AlterTableOption,
   498  ) (err error) {
   499  	var (
   500  		request = Ydb_Table.AlterTableRequest{
   501  			SessionId: s.id,
   502  			Path:      path,
   503  			OperationParams: operation.Params(
   504  				ctx,
   505  				s.config.OperationTimeout(),
   506  				s.config.OperationCancelAfter(),
   507  				operation.ModeSync,
   508  			),
   509  		}
   510  		a = allocator.New()
   511  	)
   512  	defer a.Free()
   513  	for _, opt := range opts {
   514  		if opt != nil {
   515  			opt.ApplyAlterTableOption((*options.AlterTableDesc)(&request), a)
   516  		}
   517  	}
   518  	_, err = s.tableService.AlterTable(ctx, &request)
   519  
   520  	return xerrors.WithStackTrace(err)
   521  }
   522  
   523  // CopyTable creates copy of table at given path.
   524  func (s *session) CopyTable(
   525  	ctx context.Context,
   526  	dst, src string,
   527  	opts ...options.CopyTableOption,
   528  ) (err error) {
   529  	request := Ydb_Table.CopyTableRequest{
   530  		SessionId:       s.id,
   531  		SourcePath:      src,
   532  		DestinationPath: dst,
   533  		OperationParams: operation.Params(
   534  			ctx,
   535  			s.config.OperationTimeout(),
   536  			s.config.OperationCancelAfter(),
   537  			operation.ModeSync,
   538  		),
   539  	}
   540  	for _, opt := range opts {
   541  		if opt != nil {
   542  			opt((*options.CopyTableDesc)(&request))
   543  		}
   544  	}
   545  	_, err = s.tableService.CopyTable(ctx, &request)
   546  	if err != nil {
   547  		return xerrors.WithStackTrace(err)
   548  	}
   549  
   550  	return nil
   551  }
   552  
   553  func copyTables(
   554  	ctx context.Context,
   555  	sessionID string,
   556  	operationTimeout time.Duration,
   557  	operationCancelAfter time.Duration,
   558  	service interface {
   559  		CopyTables(
   560  			ctx context.Context, in *Ydb_Table.CopyTablesRequest, opts ...grpc.CallOption,
   561  		) (*Ydb_Table.CopyTablesResponse, error)
   562  	},
   563  	opts ...options.CopyTablesOption,
   564  ) (err error) {
   565  	request := Ydb_Table.CopyTablesRequest{
   566  		SessionId: sessionID,
   567  		OperationParams: operation.Params(
   568  			ctx,
   569  			operationTimeout,
   570  			operationCancelAfter,
   571  			operation.ModeSync,
   572  		),
   573  	}
   574  	for _, opt := range opts {
   575  		if opt != nil {
   576  			opt((*options.CopyTablesDesc)(&request))
   577  		}
   578  	}
   579  	if len(request.GetTables()) == 0 {
   580  		return xerrors.WithStackTrace(fmt.Errorf("no CopyTablesItem: %w", errParamsRequired))
   581  	}
   582  	_, err = service.CopyTables(ctx, &request)
   583  	if err != nil {
   584  		return xerrors.WithStackTrace(err)
   585  	}
   586  
   587  	return nil
   588  }
   589  
   590  // CopyTables creates copy of table at given path.
   591  func (s *session) CopyTables(
   592  	ctx context.Context,
   593  	opts ...options.CopyTablesOption,
   594  ) (err error) {
   595  	err = copyTables(ctx, s.id, s.config.OperationTimeout(), s.config.OperationCancelAfter(), s.tableService, opts...)
   596  	if err != nil {
   597  		return xerrors.WithStackTrace(err)
   598  	}
   599  
   600  	return nil
   601  }
   602  
   603  // Explain explains data query represented by text.
   604  func (s *session) Explain(
   605  	ctx context.Context,
   606  	query string,
   607  ) (
   608  	exp table.DataQueryExplanation,
   609  	err error,
   610  ) {
   611  	var (
   612  		result   Ydb_Table.ExplainQueryResult
   613  		response *Ydb_Table.ExplainDataQueryResponse
   614  		onDone   = trace.TableOnSessionQueryExplain(
   615  			s.config.Trace(), &ctx,
   616  			stack.FunctionID(""),
   617  			s, query,
   618  		)
   619  	)
   620  	defer func() {
   621  		if err != nil {
   622  			onDone("", "", err)
   623  		} else {
   624  			onDone(exp.AST, exp.AST, nil)
   625  		}
   626  	}()
   627  
   628  	response, err = s.tableService.ExplainDataQuery(ctx,
   629  		&Ydb_Table.ExplainDataQueryRequest{
   630  			SessionId: s.id,
   631  			YqlText:   query,
   632  			OperationParams: operation.Params(
   633  				ctx,
   634  				s.config.OperationTimeout(),
   635  				s.config.OperationCancelAfter(),
   636  				operation.ModeSync,
   637  			),
   638  		},
   639  	)
   640  	if err != nil {
   641  		return exp, xerrors.WithStackTrace(err)
   642  	}
   643  
   644  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   645  	if err != nil {
   646  		return exp, xerrors.WithStackTrace(err)
   647  	}
   648  
   649  	return table.DataQueryExplanation{
   650  		Explanation: table.Explanation{
   651  			Plan: result.GetQueryPlan(),
   652  		},
   653  		AST: result.GetQueryAst(),
   654  	}, nil
   655  }
   656  
   657  // Prepare prepares data query within session s.
   658  func (s *session) Prepare(ctx context.Context, queryText string) (_ table.Statement, err error) {
   659  	var (
   660  		stmt     *statement
   661  		response *Ydb_Table.PrepareDataQueryResponse
   662  		result   Ydb_Table.PrepareQueryResult
   663  		onDone   = trace.TableOnSessionQueryPrepare(
   664  			s.config.Trace(), &ctx,
   665  			stack.FunctionID(""),
   666  			s, queryText,
   667  		)
   668  	)
   669  	defer func() {
   670  		if err != nil {
   671  			onDone(nil, err)
   672  		} else {
   673  			onDone(stmt.query, nil)
   674  		}
   675  	}()
   676  
   677  	response, err = s.tableService.PrepareDataQuery(ctx,
   678  		&Ydb_Table.PrepareDataQueryRequest{
   679  			SessionId: s.id,
   680  			YqlText:   queryText,
   681  			OperationParams: operation.Params(
   682  				ctx,
   683  				s.config.OperationTimeout(),
   684  				s.config.OperationCancelAfter(),
   685  				operation.ModeSync,
   686  			),
   687  		},
   688  	)
   689  	if err != nil {
   690  		return nil, xerrors.WithStackTrace(err)
   691  	}
   692  
   693  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   694  	if err != nil {
   695  		return nil, xerrors.WithStackTrace(err)
   696  	}
   697  
   698  	stmt = &statement{
   699  		session: s,
   700  		query:   queryPrepared(result.GetQueryId(), queryText),
   701  		params:  result.GetParametersTypes(),
   702  	}
   703  
   704  	return stmt, nil
   705  }
   706  
   707  // Execute executes given data query represented by text.
   708  func (s *session) Execute(
   709  	ctx context.Context,
   710  	txControl *table.TransactionControl,
   711  	query string,
   712  	parameters *params.Parameters,
   713  	opts ...options.ExecuteDataQueryOption,
   714  ) (
   715  	txr table.Transaction, r result.Result, err error,
   716  ) {
   717  	var (
   718  		a       = allocator.New()
   719  		q       = queryFromText(query)
   720  		request = options.ExecuteDataQueryDesc{
   721  			ExecuteDataQueryRequest: a.TableExecuteDataQueryRequest(),
   722  			IgnoreTruncated:         s.config.IgnoreTruncated(),
   723  		}
   724  		callOptions []grpc.CallOption
   725  	)
   726  	defer a.Free()
   727  
   728  	request.SessionId = s.id
   729  	request.TxControl = txControl.Desc()
   730  	request.Parameters = parameters.ToYDB(a)
   731  	request.Query = q.toYDB(a)
   732  	request.QueryCachePolicy = a.TableQueryCachePolicy()
   733  	request.QueryCachePolicy.KeepInCache = len(request.Parameters) > 0
   734  	request.OperationParams = operation.Params(ctx,
   735  		s.config.OperationTimeout(),
   736  		s.config.OperationCancelAfter(),
   737  		operation.ModeSync,
   738  	)
   739  
   740  	for _, opt := range opts {
   741  		if opt != nil {
   742  			callOptions = append(callOptions, opt.ApplyExecuteDataQueryOption(&request, a)...)
   743  		}
   744  	}
   745  
   746  	onDone := trace.TableOnSessionQueryExecute(
   747  		s.config.Trace(), &ctx,
   748  		stack.FunctionID(""),
   749  		s, q, parameters,
   750  		request.QueryCachePolicy.GetKeepInCache(),
   751  	)
   752  	defer func() {
   753  		onDone(txr, false, r, err)
   754  	}()
   755  
   756  	result, err := s.executeDataQuery(ctx, a, request.ExecuteDataQueryRequest, callOptions...)
   757  	if err != nil {
   758  		return nil, nil, xerrors.WithStackTrace(err)
   759  	}
   760  
   761  	return s.executeQueryResult(result, request.TxControl, request.IgnoreTruncated)
   762  }
   763  
   764  // executeQueryResult returns Transaction and result built from received
   765  // result.
   766  func (s *session) executeQueryResult(
   767  	res *Ydb_Table.ExecuteQueryResult,
   768  	txControl *Ydb_Table.TransactionControl,
   769  	ignoreTruncated bool,
   770  ) (
   771  	table.Transaction, result.Result, error,
   772  ) {
   773  	tx := &transaction{
   774  		id: res.GetTxMeta().GetId(),
   775  		s:  s,
   776  	}
   777  	if txControl.GetCommitTx() {
   778  		tx.state.Store(txStateCommitted)
   779  	} else {
   780  		tx.state.Store(txStateInitialized)
   781  		tx.control = table.TxControl(table.WithTxID(tx.id))
   782  	}
   783  
   784  	return tx, scanner.NewUnary(
   785  		res.GetResultSets(),
   786  		res.GetQueryStats(),
   787  		scanner.WithIgnoreTruncated(ignoreTruncated),
   788  	), nil
   789  }
   790  
   791  // executeDataQuery executes data query.
   792  func (s *session) executeDataQuery(
   793  	ctx context.Context, a *allocator.Allocator, request *Ydb_Table.ExecuteDataQueryRequest,
   794  	callOptions ...grpc.CallOption,
   795  ) (
   796  	_ *Ydb_Table.ExecuteQueryResult,
   797  	err error,
   798  ) {
   799  	var (
   800  		result   = a.TableExecuteQueryResult()
   801  		response *Ydb_Table.ExecuteDataQueryResponse
   802  	)
   803  
   804  	response, err = s.tableService.ExecuteDataQuery(ctx, request, callOptions...)
   805  	if err != nil {
   806  		return nil, xerrors.WithStackTrace(err)
   807  	}
   808  
   809  	err = response.GetOperation().GetResult().UnmarshalTo(result)
   810  	if err != nil {
   811  		return nil, xerrors.WithStackTrace(err)
   812  	}
   813  
   814  	return result, nil
   815  }
   816  
   817  // ExecuteSchemeQuery executes scheme query.
   818  func (s *session) ExecuteSchemeQuery(
   819  	ctx context.Context,
   820  	query string,
   821  	opts ...options.ExecuteSchemeQueryOption,
   822  ) (err error) {
   823  	request := Ydb_Table.ExecuteSchemeQueryRequest{
   824  		SessionId: s.id,
   825  		YqlText:   query,
   826  		OperationParams: operation.Params(
   827  			ctx,
   828  			s.config.OperationTimeout(),
   829  			s.config.OperationCancelAfter(),
   830  			operation.ModeSync,
   831  		),
   832  	}
   833  	for _, opt := range opts {
   834  		if opt != nil {
   835  			opt((*options.ExecuteSchemeQueryDesc)(&request))
   836  		}
   837  	}
   838  	_, err = s.tableService.ExecuteSchemeQuery(ctx, &request)
   839  
   840  	return xerrors.WithStackTrace(err)
   841  }
   842  
   843  // DescribeTableOptions describes supported table options.
   844  func (s *session) DescribeTableOptions(ctx context.Context) (
   845  	desc options.TableOptionsDescription,
   846  	err error,
   847  ) {
   848  	var (
   849  		response *Ydb_Table.DescribeTableOptionsResponse
   850  		result   Ydb_Table.DescribeTableOptionsResult
   851  	)
   852  	request := Ydb_Table.DescribeTableOptionsRequest{
   853  		OperationParams: operation.Params(
   854  			ctx,
   855  			s.config.OperationTimeout(),
   856  			s.config.OperationCancelAfter(),
   857  			operation.ModeSync,
   858  		),
   859  	}
   860  	response, err = s.tableService.DescribeTableOptions(ctx, &request)
   861  	if err != nil {
   862  		return desc, xerrors.WithStackTrace(err)
   863  	}
   864  
   865  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
   866  	if err != nil {
   867  		return desc, xerrors.WithStackTrace(err)
   868  	}
   869  
   870  	{
   871  		xs := make([]options.TableProfileDescription, len(result.GetTableProfilePresets()))
   872  		for i, p := range result.GetTableProfilePresets() {
   873  			xs[i] = options.TableProfileDescription{
   874  				Name:   p.GetName(),
   875  				Labels: p.GetLabels(),
   876  
   877  				DefaultStoragePolicy:      p.GetDefaultStoragePolicy(),
   878  				DefaultCompactionPolicy:   p.GetDefaultCompactionPolicy(),
   879  				DefaultPartitioningPolicy: p.GetDefaultPartitioningPolicy(),
   880  				DefaultExecutionPolicy:    p.GetDefaultExecutionPolicy(),
   881  				DefaultReplicationPolicy:  p.GetDefaultReplicationPolicy(),
   882  				DefaultCachingPolicy:      p.GetDefaultCachingPolicy(),
   883  
   884  				AllowedStoragePolicies:      p.GetAllowedStoragePolicies(),
   885  				AllowedCompactionPolicies:   p.GetAllowedCompactionPolicies(),
   886  				AllowedPartitioningPolicies: p.GetAllowedPartitioningPolicies(),
   887  				AllowedExecutionPolicies:    p.GetAllowedExecutionPolicies(),
   888  				AllowedReplicationPolicies:  p.GetAllowedReplicationPolicies(),
   889  				AllowedCachingPolicies:      p.GetAllowedCachingPolicies(),
   890  			}
   891  		}
   892  		desc.TableProfilePresets = xs
   893  	}
   894  	{
   895  		xs := make(
   896  			[]options.StoragePolicyDescription,
   897  			len(result.GetStoragePolicyPresets()),
   898  		)
   899  		for i, p := range result.GetStoragePolicyPresets() {
   900  			xs[i] = options.StoragePolicyDescription{
   901  				Name:   p.GetName(),
   902  				Labels: p.GetLabels(),
   903  			}
   904  		}
   905  		desc.StoragePolicyPresets = xs
   906  	}
   907  	{
   908  		xs := make(
   909  			[]options.CompactionPolicyDescription,
   910  			len(result.GetCompactionPolicyPresets()),
   911  		)
   912  		for i, p := range result.GetCompactionPolicyPresets() {
   913  			xs[i] = options.CompactionPolicyDescription{
   914  				Name:   p.GetName(),
   915  				Labels: p.GetLabels(),
   916  			}
   917  		}
   918  		desc.CompactionPolicyPresets = xs
   919  	}
   920  	{
   921  		xs := make(
   922  			[]options.PartitioningPolicyDescription,
   923  			len(result.GetPartitioningPolicyPresets()),
   924  		)
   925  		for i, p := range result.GetPartitioningPolicyPresets() {
   926  			xs[i] = options.PartitioningPolicyDescription{
   927  				Name:   p.GetName(),
   928  				Labels: p.GetLabels(),
   929  			}
   930  		}
   931  		desc.PartitioningPolicyPresets = xs
   932  	}
   933  	{
   934  		xs := make(
   935  			[]options.ExecutionPolicyDescription,
   936  			len(result.GetExecutionPolicyPresets()),
   937  		)
   938  		for i, p := range result.GetExecutionPolicyPresets() {
   939  			xs[i] = options.ExecutionPolicyDescription{
   940  				Name:   p.GetName(),
   941  				Labels: p.GetLabels(),
   942  			}
   943  		}
   944  		desc.ExecutionPolicyPresets = xs
   945  	}
   946  	{
   947  		xs := make(
   948  			[]options.ReplicationPolicyDescription,
   949  			len(result.GetReplicationPolicyPresets()),
   950  		)
   951  		for i, p := range result.GetReplicationPolicyPresets() {
   952  			xs[i] = options.ReplicationPolicyDescription{
   953  				Name:   p.GetName(),
   954  				Labels: p.GetLabels(),
   955  			}
   956  		}
   957  		desc.ReplicationPolicyPresets = xs
   958  	}
   959  	{
   960  		xs := make(
   961  			[]options.CachingPolicyDescription,
   962  			len(result.GetCachingPolicyPresets()),
   963  		)
   964  		for i, p := range result.GetCachingPolicyPresets() {
   965  			xs[i] = options.CachingPolicyDescription{
   966  				Name:   p.GetName(),
   967  				Labels: p.GetLabels(),
   968  			}
   969  		}
   970  		desc.CachingPolicyPresets = xs
   971  	}
   972  
   973  	return desc, nil
   974  }
   975  
   976  // StreamReadTable reads table at given path with given options.
   977  //
   978  // Note that given ctx controls the lifetime of the whole read, not only this
   979  // StreamReadTable() call; that is, the time until returned result is closed
   980  // via Close() call or fully drained by sequential NextResultSet() calls.
   981  func (s *session) StreamReadTable(
   982  	ctx context.Context,
   983  	path string,
   984  	opts ...options.ReadTableOption,
   985  ) (_ result.StreamResult, err error) {
   986  	var (
   987  		onIntermediate = trace.TableOnSessionQueryStreamRead(s.config.Trace(), &ctx,
   988  			stack.FunctionID(""),
   989  			s,
   990  		)
   991  		request = Ydb_Table.ReadTableRequest{
   992  			SessionId: s.id,
   993  			Path:      path,
   994  		}
   995  		stream Ydb_Table_V1.TableService_StreamReadTableClient
   996  		a      = allocator.New()
   997  	)
   998  	defer func() {
   999  		a.Free()
  1000  		if err != nil {
  1001  			onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
  1002  		}
  1003  	}()
  1004  
  1005  	for _, opt := range opts {
  1006  		if opt != nil {
  1007  			opt.ApplyReadTableOption((*options.ReadTableDesc)(&request), a)
  1008  		}
  1009  	}
  1010  
  1011  	ctx, cancel := xcontext.WithCancel(ctx)
  1012  
  1013  	stream, err = s.tableService.StreamReadTable(ctx, &request)
  1014  	if err != nil {
  1015  		cancel()
  1016  
  1017  		return nil, xerrors.WithStackTrace(err)
  1018  	}
  1019  
  1020  	return scanner.NewStream(ctx,
  1021  		func(ctx context.Context) (
  1022  			set *Ydb.ResultSet,
  1023  			stats *Ydb_TableStats.QueryStats,
  1024  			err error,
  1025  		) {
  1026  			defer func() {
  1027  				onIntermediate(xerrors.HideEOF(err))
  1028  			}()
  1029  			select {
  1030  			case <-ctx.Done():
  1031  				return nil, nil, xerrors.WithStackTrace(ctx.Err())
  1032  			default:
  1033  				var response *Ydb_Table.ReadTableResponse
  1034  				response, err = stream.Recv()
  1035  				result := response.GetResult()
  1036  				if result == nil || err != nil {
  1037  					return nil, nil, xerrors.WithStackTrace(err)
  1038  				}
  1039  
  1040  				return result.GetResultSet(), nil, nil
  1041  			}
  1042  		},
  1043  		func(err error) error {
  1044  			cancel()
  1045  			onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
  1046  
  1047  			return err
  1048  		},
  1049  		scanner.WithIgnoreTruncated(true), // stream read table always returns truncated flag on last result set
  1050  	)
  1051  }
  1052  
  1053  func (s *session) ReadRows(
  1054  	ctx context.Context,
  1055  	path string,
  1056  	keys value.Value,
  1057  	opts ...options.ReadRowsOption,
  1058  ) (_ result.Result, err error) {
  1059  	var (
  1060  		a       = allocator.New()
  1061  		request = Ydb_Table.ReadRowsRequest{
  1062  			SessionId: s.id,
  1063  			Path:      path,
  1064  			Keys:      value.ToYDB(keys, a),
  1065  		}
  1066  		response *Ydb_Table.ReadRowsResponse
  1067  	)
  1068  	defer func() {
  1069  		a.Free()
  1070  	}()
  1071  
  1072  	for _, opt := range opts {
  1073  		if opt != nil {
  1074  			opt.ApplyReadRowsOption((*options.ReadRowsDesc)(&request), a)
  1075  		}
  1076  	}
  1077  
  1078  	response, err = s.tableService.ReadRows(ctx, &request)
  1079  	if err != nil {
  1080  		return nil, xerrors.WithStackTrace(err)
  1081  	}
  1082  
  1083  	if response.GetStatus() != Ydb.StatusIds_SUCCESS {
  1084  		return nil, xerrors.WithStackTrace(
  1085  			xerrors.FromOperation(response),
  1086  		)
  1087  	}
  1088  
  1089  	return scanner.NewUnary(
  1090  		[]*Ydb.ResultSet{response.GetResultSet()},
  1091  		nil,
  1092  		scanner.WithIgnoreTruncated(s.config.IgnoreTruncated()),
  1093  	), nil
  1094  }
  1095  
  1096  // StreamExecuteScanQuery scan-reads table at given path with given options.
  1097  //
  1098  // Note that given ctx controls the lifetime of the whole read, not only this
  1099  // StreamExecuteScanQuery() call; that is, the time until returned result is closed
  1100  // via Close() call or fully drained by sequential NextResultSet() calls.
  1101  func (s *session) StreamExecuteScanQuery(
  1102  	ctx context.Context,
  1103  	query string,
  1104  	parameters *params.Parameters,
  1105  	opts ...options.ExecuteScanQueryOption,
  1106  ) (_ result.StreamResult, err error) {
  1107  	var (
  1108  		a              = allocator.New()
  1109  		q              = queryFromText(query)
  1110  		onIntermediate = trace.TableOnSessionQueryStreamExecute(
  1111  			s.config.Trace(), &ctx,
  1112  			stack.FunctionID(""),
  1113  			s, q, parameters,
  1114  		)
  1115  		request = Ydb_Table.ExecuteScanQueryRequest{
  1116  			Query:      q.toYDB(a),
  1117  			Parameters: parameters.ToYDB(a),
  1118  			Mode:       Ydb_Table.ExecuteScanQueryRequest_MODE_EXEC, // set default
  1119  		}
  1120  		stream      Ydb_Table_V1.TableService_StreamExecuteScanQueryClient
  1121  		callOptions []grpc.CallOption
  1122  	)
  1123  	defer func() {
  1124  		a.Free()
  1125  		if err != nil {
  1126  			onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
  1127  		}
  1128  	}()
  1129  
  1130  	for _, opt := range opts {
  1131  		if opt != nil {
  1132  			callOptions = append(callOptions, opt.ApplyExecuteScanQueryOption((*options.ExecuteScanQueryDesc)(&request))...)
  1133  		}
  1134  	}
  1135  
  1136  	ctx, cancel := xcontext.WithCancel(ctx)
  1137  
  1138  	stream, err = s.tableService.StreamExecuteScanQuery(ctx, &request, callOptions...)
  1139  	if err != nil {
  1140  		cancel()
  1141  
  1142  		return nil, xerrors.WithStackTrace(err)
  1143  	}
  1144  
  1145  	return scanner.NewStream(ctx,
  1146  		func(ctx context.Context) (
  1147  			set *Ydb.ResultSet,
  1148  			stats *Ydb_TableStats.QueryStats,
  1149  			err error,
  1150  		) {
  1151  			defer func() {
  1152  				onIntermediate(xerrors.HideEOF(err))
  1153  			}()
  1154  			select {
  1155  			case <-ctx.Done():
  1156  				return nil, nil, xerrors.WithStackTrace(ctx.Err())
  1157  			default:
  1158  				var response *Ydb_Table.ExecuteScanQueryPartialResponse
  1159  				response, err = stream.Recv()
  1160  				result := response.GetResult()
  1161  				if result == nil || err != nil {
  1162  					return nil, nil, xerrors.WithStackTrace(err)
  1163  				}
  1164  
  1165  				return result.GetResultSet(), result.GetQueryStats(), nil
  1166  			}
  1167  		},
  1168  		func(err error) error {
  1169  			cancel()
  1170  			onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
  1171  
  1172  			return err
  1173  		},
  1174  		scanner.WithIgnoreTruncated(s.config.IgnoreTruncated()),
  1175  		scanner.WithMarkTruncatedAsRetryable(),
  1176  	)
  1177  }
  1178  
  1179  // BulkUpsert uploads given list of ydb struct values to the table.
  1180  func (s *session) BulkUpsert(ctx context.Context, table string, rows value.Value,
  1181  	opts ...options.BulkUpsertOption,
  1182  ) (err error) {
  1183  	var (
  1184  		a           = allocator.New()
  1185  		callOptions []grpc.CallOption
  1186  		onDone      = trace.TableOnSessionBulkUpsert(
  1187  			s.config.Trace(), &ctx,
  1188  			stack.FunctionID(""),
  1189  			s,
  1190  		)
  1191  	)
  1192  	defer func() {
  1193  		defer a.Free()
  1194  		onDone(err)
  1195  	}()
  1196  
  1197  	for _, opt := range opts {
  1198  		callOptions = append(callOptions, opt.ApplyBulkUpsertOption()...)
  1199  	}
  1200  
  1201  	_, err = s.tableService.BulkUpsert(ctx,
  1202  		&Ydb_Table.BulkUpsertRequest{
  1203  			Table: table,
  1204  			Rows:  value.ToYDB(rows, a),
  1205  			OperationParams: operation.Params(
  1206  				ctx,
  1207  				s.config.OperationTimeout(),
  1208  				s.config.OperationCancelAfter(),
  1209  				operation.ModeSync,
  1210  			),
  1211  		},
  1212  		callOptions...,
  1213  	)
  1214  	if err != nil {
  1215  		return xerrors.WithStackTrace(err)
  1216  	}
  1217  
  1218  	return nil
  1219  }
  1220  
  1221  // BeginTransaction begins new transaction within given session with given
  1222  // settings.
  1223  func (s *session) BeginTransaction(
  1224  	ctx context.Context,
  1225  	txSettings *table.TransactionSettings,
  1226  ) (x table.Transaction, err error) {
  1227  	var (
  1228  		result   Ydb_Table.BeginTransactionResult
  1229  		response *Ydb_Table.BeginTransactionResponse
  1230  		onDone   = trace.TableOnSessionTransactionBegin(
  1231  			s.config.Trace(), &ctx,
  1232  			stack.FunctionID(""),
  1233  			s,
  1234  		)
  1235  	)
  1236  	defer func() {
  1237  		onDone(x, err)
  1238  	}()
  1239  
  1240  	response, err = s.tableService.BeginTransaction(ctx,
  1241  		&Ydb_Table.BeginTransactionRequest{
  1242  			SessionId:  s.id,
  1243  			TxSettings: txSettings.Settings(),
  1244  			OperationParams: operation.Params(
  1245  				ctx,
  1246  				s.config.OperationTimeout(),
  1247  				s.config.OperationCancelAfter(),
  1248  				operation.ModeSync,
  1249  			),
  1250  		},
  1251  	)
  1252  	if err != nil {
  1253  		return nil, xerrors.WithStackTrace(err)
  1254  	}
  1255  	err = response.GetOperation().GetResult().UnmarshalTo(&result)
  1256  	if err != nil {
  1257  		return nil, xerrors.WithStackTrace(err)
  1258  	}
  1259  	tx := &transaction{
  1260  		id:      result.GetTxMeta().GetId(),
  1261  		s:       s,
  1262  		control: table.TxControl(table.WithTxID(result.GetTxMeta().GetId())),
  1263  	}
  1264  	tx.state.Store(txStateInitialized)
  1265  
  1266  	return tx, nil
  1267  }