github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/query/client_test.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"math/rand"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
    12  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    13  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations"
    14  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
    15  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats"
    16  	"go.uber.org/mock/gomock"
    17  	"google.golang.org/grpc"
    18  	grpcCodes "google.golang.org/grpc/codes"
    19  	grpcStatus "google.golang.org/grpc/status"
    20  	"google.golang.org/protobuf/types/known/anypb"
    21  
    22  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/pool"
    23  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
    24  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/query/session"
    25  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/query/tx"
    26  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
    27  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    28  	"github.com/ydb-platform/ydb-go-sdk/v3/query"
    29  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    30  )
    31  
    32  func TestClient(t *testing.T) {
    33  	ctx := xtest.Context(t)
    34  	t.Run("createSession", func(t *testing.T) {
    35  		t.Run("HappyWay", func(t *testing.T) {
    36  			ctrl := gomock.NewController(t)
    37  			attachStream := NewMockQueryService_AttachSessionClient(ctrl)
    38  			attachStream.EXPECT().Recv().Return(&Ydb_Query.SessionState{
    39  				Status: Ydb.StatusIds_SUCCESS,
    40  			}, nil).AnyTimes()
    41  			client := NewMockQueryServiceClient(ctrl)
    42  			client.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
    43  				Status:    Ydb.StatusIds_SUCCESS,
    44  				SessionId: "test",
    45  			}, nil)
    46  			client.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(attachStream, nil)
    47  			client.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.DeleteSessionResponse{
    48  				Status: Ydb.StatusIds_SUCCESS,
    49  			}, nil)
    50  			attached := 0
    51  			s, err := createSession(ctx, client, session.WithTrace(
    52  				&trace.Query{
    53  					OnSessionAttach: func(info trace.QuerySessionAttachStartInfo) func(info trace.QuerySessionAttachDoneInfo) {
    54  						return func(info trace.QuerySessionAttachDoneInfo) {
    55  							if info.Error == nil {
    56  								attached++
    57  							}
    58  						}
    59  					},
    60  					OnSessionDelete: func(info trace.QuerySessionDeleteStartInfo) func(info trace.QuerySessionDeleteDoneInfo) {
    61  						attached--
    62  
    63  						return nil
    64  					},
    65  				},
    66  			))
    67  			require.NoError(t, err)
    68  			require.EqualValues(t, "test", s.ID())
    69  			require.EqualValues(t, 1, attached)
    70  			err = s.Close(ctx)
    71  			require.NoError(t, err)
    72  			require.EqualValues(t, 0, attached)
    73  		})
    74  		t.Run("TransportError", func(t *testing.T) {
    75  			t.Run("OnCall", func(t *testing.T) {
    76  				ctrl := gomock.NewController(t)
    77  				client := NewMockQueryServiceClient(ctrl)
    78  				client.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
    79  				_, err := createSession(ctx, client)
    80  				require.Error(t, err)
    81  				require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
    82  			})
    83  			t.Run("OnAttach", func(t *testing.T) {
    84  				ctrl := gomock.NewController(t)
    85  				client := NewMockQueryServiceClient(ctrl)
    86  				client.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
    87  					Status:    Ydb.StatusIds_SUCCESS,
    88  					SessionId: "test",
    89  				}, nil)
    90  				client.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
    91  				client.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
    92  				_, err := createSession(ctx, client)
    93  				require.Error(t, err)
    94  				require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
    95  			})
    96  			t.Run("OnRecv", func(t *testing.T) {
    97  				ctrl := gomock.NewController(t)
    98  				attachStream := NewMockQueryService_AttachSessionClient(ctrl)
    99  				attachStream.EXPECT().Recv().Return(nil, grpcStatus.Error(grpcCodes.Unavailable, "")).AnyTimes()
   100  				client := NewMockQueryServiceClient(ctrl)
   101  				client.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
   102  					Status:    Ydb.StatusIds_SUCCESS,
   103  					SessionId: "test",
   104  				}, nil)
   105  				client.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(attachStream, nil)
   106  				client.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.DeleteSessionResponse{
   107  					Status: Ydb.StatusIds_SUCCESS,
   108  				}, nil)
   109  				_, err := createSession(ctx, client)
   110  				require.Error(t, err)
   111  				require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
   112  			})
   113  		})
   114  		t.Run("OperationError", func(t *testing.T) {
   115  			t.Run("OnCall", func(t *testing.T) {
   116  				ctrl := gomock.NewController(t)
   117  				client := NewMockQueryServiceClient(ctrl)
   118  				client.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil,
   119  					xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
   120  				)
   121  				_, err := createSession(ctx, client)
   122  				require.Error(t, err)
   123  				require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
   124  			})
   125  			t.Run("OnRecv", func(t *testing.T) {
   126  				ctrl := gomock.NewController(t)
   127  				attachStream := NewMockQueryService_AttachSessionClient(ctrl)
   128  				attachStream.EXPECT().Recv().Return(nil,
   129  					xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
   130  				)
   131  				client := NewMockQueryServiceClient(ctrl)
   132  				client.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
   133  					Status:    Ydb.StatusIds_SUCCESS,
   134  					SessionId: "test",
   135  				}, nil)
   136  				client.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(attachStream, nil)
   137  				client.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.DeleteSessionResponse{
   138  					Status: Ydb.StatusIds_SUCCESS,
   139  				}, nil)
   140  				_, err := createSession(ctx, client)
   141  				require.Error(t, err)
   142  				require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
   143  			})
   144  		})
   145  	})
   146  	t.Run("Do", func(t *testing.T) {
   147  		t.Run("HappyWay", func(t *testing.T) {
   148  			var visited bool
   149  			err := do(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   150  				return newTestSession("123"), nil
   151  			}), func(ctx context.Context, s *Session) error {
   152  				visited = true
   153  
   154  				return nil
   155  			})
   156  			require.NoError(t, err)
   157  			require.True(t, visited)
   158  		})
   159  		t.Run("RetryableError", func(t *testing.T) {
   160  			counter := 0
   161  			err := do(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   162  				return newTestSession("123"), nil
   163  			}), func(ctx context.Context, s *Session) error {
   164  				counter++
   165  				if counter < 10 {
   166  					return xerrors.Retryable(errors.New(""))
   167  				}
   168  
   169  				return nil
   170  			})
   171  			require.NoError(t, err)
   172  			require.Equal(t, 10, counter)
   173  		})
   174  	})
   175  	t.Run("DoTx", func(t *testing.T) {
   176  		t.Run("HappyWay", func(t *testing.T) {
   177  			t.Run("LazyTx", func(t *testing.T) {
   178  				ctrl := gomock.NewController(t)
   179  				err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   180  					client := NewMockQueryServiceClient(ctrl)
   181  					stream := NewMockQueryService_ExecuteQueryClient(ctrl)
   182  					stream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   183  						client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CommitTransactionResponse{
   184  							Status: Ydb.StatusIds_SUCCESS,
   185  						}, nil)
   186  
   187  						return &Ydb_Query.ExecuteQueryResponsePart{
   188  							Status: Ydb.StatusIds_SUCCESS,
   189  							TxMeta: &Ydb_Query.TransactionMeta{
   190  								Id: "456",
   191  							},
   192  							ResultSetIndex: 0,
   193  							ResultSet: &Ydb.ResultSet{
   194  								Columns: []*Ydb.Column{
   195  									{
   196  										Name: "a",
   197  										Type: &Ydb.Type{
   198  											Type: &Ydb.Type_TypeId{
   199  												TypeId: Ydb.Type_UINT64,
   200  											},
   201  										},
   202  									},
   203  									{
   204  										Name: "b",
   205  										Type: &Ydb.Type{
   206  											Type: &Ydb.Type_TypeId{
   207  												TypeId: Ydb.Type_UTF8,
   208  											},
   209  										},
   210  									},
   211  								},
   212  								Rows: []*Ydb.Value{
   213  									{
   214  										Items: []*Ydb.Value{{
   215  											Value: &Ydb.Value_Uint64Value{
   216  												Uint64Value: 1,
   217  											},
   218  										}, {
   219  											Value: &Ydb.Value_TextValue{
   220  												TextValue: "1",
   221  											},
   222  										}},
   223  									},
   224  									{
   225  										Items: []*Ydb.Value{{
   226  											Value: &Ydb.Value_Uint64Value{
   227  												Uint64Value: 2,
   228  											},
   229  										}, {
   230  											Value: &Ydb.Value_TextValue{
   231  												TextValue: "2",
   232  											},
   233  										}},
   234  									},
   235  									{
   236  										Items: []*Ydb.Value{{
   237  											Value: &Ydb.Value_Uint64Value{
   238  												Uint64Value: 3,
   239  											},
   240  										}, {
   241  											Value: &Ydb.Value_TextValue{
   242  												TextValue: "3",
   243  											},
   244  										}},
   245  									},
   246  								},
   247  							},
   248  						}, nil
   249  					})
   250  					stream.EXPECT().Recv().Return(nil, io.EOF)
   251  					client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
   252  
   253  					return newTestSessionWithClient("123", client, true), nil
   254  				}), func(ctx context.Context, tx query.TxActor) error {
   255  					defer func() {
   256  						require.Equal(t, "456", tx.ID())
   257  					}()
   258  
   259  					return tx.Exec(ctx, "")
   260  				}, tx.NewSettings(tx.WithDefaultTxMode()))
   261  				require.NoError(t, err)
   262  			})
   263  			t.Run("NoLazyTx", func(t *testing.T) {
   264  				ctrl := gomock.NewController(t)
   265  				err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   266  					client := NewMockQueryServiceClient(ctrl)
   267  					client.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).DoAndReturn(
   268  						func(ctx context.Context, request *Ydb_Query.BeginTransactionRequest, option ...grpc.CallOption) (
   269  							*Ydb_Query.BeginTransactionResponse, error,
   270  						) {
   271  							client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   272  								func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   273  									Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   274  								) {
   275  									stream := NewMockQueryService_ExecuteQueryClient(ctrl)
   276  									stream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   277  										client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CommitTransactionResponse{
   278  											Status: Ydb.StatusIds_SUCCESS,
   279  										}, nil)
   280  
   281  										return &Ydb_Query.ExecuteQueryResponsePart{
   282  											Status: Ydb.StatusIds_SUCCESS,
   283  											TxMeta: &Ydb_Query.TransactionMeta{
   284  												Id: "456",
   285  											},
   286  											ResultSetIndex: 0,
   287  											ResultSet: &Ydb.ResultSet{
   288  												Columns: []*Ydb.Column{
   289  													{
   290  														Name: "a",
   291  														Type: &Ydb.Type{
   292  															Type: &Ydb.Type_TypeId{
   293  																TypeId: Ydb.Type_UINT64,
   294  															},
   295  														},
   296  													},
   297  													{
   298  														Name: "b",
   299  														Type: &Ydb.Type{
   300  															Type: &Ydb.Type_TypeId{
   301  																TypeId: Ydb.Type_UTF8,
   302  															},
   303  														},
   304  													},
   305  												},
   306  												Rows: []*Ydb.Value{
   307  													{
   308  														Items: []*Ydb.Value{{
   309  															Value: &Ydb.Value_Uint64Value{
   310  																Uint64Value: 1,
   311  															},
   312  														}, {
   313  															Value: &Ydb.Value_TextValue{
   314  																TextValue: "1",
   315  															},
   316  														}},
   317  													},
   318  													{
   319  														Items: []*Ydb.Value{{
   320  															Value: &Ydb.Value_Uint64Value{
   321  																Uint64Value: 2,
   322  															},
   323  														}, {
   324  															Value: &Ydb.Value_TextValue{
   325  																TextValue: "2",
   326  															},
   327  														}},
   328  													},
   329  													{
   330  														Items: []*Ydb.Value{{
   331  															Value: &Ydb.Value_Uint64Value{
   332  																Uint64Value: 3,
   333  															},
   334  														}, {
   335  															Value: &Ydb.Value_TextValue{
   336  																TextValue: "3",
   337  															},
   338  														}},
   339  													},
   340  												},
   341  											},
   342  										}, nil
   343  									})
   344  									stream.EXPECT().Recv().Return(nil, io.EOF)
   345  
   346  									return stream, nil
   347  								},
   348  							)
   349  
   350  							return &Ydb_Query.BeginTransactionResponse{
   351  								Status: Ydb.StatusIds_SUCCESS,
   352  								TxMeta: &Ydb_Query.TransactionMeta{Id: "456"},
   353  							}, nil
   354  						},
   355  					)
   356  
   357  					return newTestSessionWithClient("123", client, false), nil
   358  				}), func(ctx context.Context, tx query.TxActor) error {
   359  					defer func() {
   360  						require.Equal(t, "456", tx.ID())
   361  					}()
   362  
   363  					return tx.Exec(ctx, "")
   364  				}, tx.NewSettings(tx.WithDefaultTxMode()))
   365  				require.NoError(t, err)
   366  			})
   367  		})
   368  		t.Run("RetryableError", func(t *testing.T) {
   369  			counter := 0
   370  			ctrl := gomock.NewController(t)
   371  			client := NewMockQueryServiceClient(ctrl)
   372  			client.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.BeginTransactionResponse{
   373  				Status: Ydb.StatusIds_SUCCESS,
   374  			}, nil).AnyTimes()
   375  			client.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.RollbackTransactionResponse{
   376  				Status: Ydb.StatusIds_SUCCESS,
   377  			}, nil).AnyTimes()
   378  			client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CommitTransactionResponse{
   379  				Status: Ydb.StatusIds_SUCCESS,
   380  			}, nil).AnyTimes()
   381  			err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   382  				return newTestSessionWithClient("123", client, true), nil
   383  			}), func(ctx context.Context, tx query.TxActor) error {
   384  				counter++
   385  				if counter < 10 {
   386  					return xerrors.Retryable(errors.New(""))
   387  				}
   388  
   389  				return nil
   390  			}, tx.NewSettings(tx.WithDefaultTxMode()))
   391  			require.NoError(t, err)
   392  			require.Equal(t, 10, counter)
   393  		})
   394  		t.Run("TxLeak", func(t *testing.T) {
   395  			t.Run("OnExec", func(t *testing.T) {
   396  				t.Run("WithoutCommit", func(t *testing.T) {
   397  					xtest.TestManyTimes(t, func(t testing.TB) {
   398  						txInFlight := 0
   399  						ctrl := gomock.NewController(t)
   400  						err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   401  							client := NewMockQueryServiceClient(ctrl)
   402  							client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   403  								func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   404  									Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   405  								) {
   406  									if rand.Int31n(100) < 50 {
   407  										return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   408  									}
   409  
   410  									txInFlight++
   411  
   412  									stream := NewMockQueryService_ExecuteQueryClient(ctrl)
   413  									stream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   414  										stream.EXPECT().Recv().Return(nil, io.EOF)
   415  										client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).DoAndReturn(
   416  											func(
   417  												ctx context.Context, request *Ydb_Query.CommitTransactionRequest,
   418  												option ...grpc.CallOption,
   419  											) (
   420  												*Ydb_Query.CommitTransactionResponse, error,
   421  											) {
   422  												txInFlight--
   423  
   424  												return &Ydb_Query.CommitTransactionResponse{
   425  													Status: Ydb.StatusIds_SUCCESS,
   426  												}, nil
   427  											})
   428  
   429  										return &Ydb_Query.ExecuteQueryResponsePart{
   430  											Status: Ydb.StatusIds_SUCCESS,
   431  											TxMeta: &Ydb_Query.TransactionMeta{
   432  												Id: "456",
   433  											},
   434  											ExecStats: &Ydb_TableStats.QueryStats{},
   435  										}, nil
   436  									})
   437  
   438  									return stream, nil
   439  								})
   440  
   441  							return newTestSessionWithClient("123", client, true), nil
   442  						}), func(ctx context.Context, tx query.TxActor) error {
   443  							return tx.Exec(ctx, "")
   444  						}, tx.NewSettings(tx.WithSerializableReadWrite()))
   445  						require.NoError(t, err)
   446  						require.Zero(t, txInFlight)
   447  					})
   448  				})
   449  				t.Run("WithCommit", func(t *testing.T) {
   450  					xtest.TestManyTimes(t, func(t testing.TB) {
   451  						ctrl := gomock.NewController(t)
   452  						txInFlight := 0
   453  						err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   454  							client := NewMockQueryServiceClient(ctrl)
   455  							client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   456  								func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   457  									Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   458  								) {
   459  									require.True(t, request.GetTxControl().GetCommitTx())
   460  
   461  									if rand.Int31n(100) < 50 {
   462  										return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   463  									}
   464  
   465  									txInFlight++
   466  
   467  									stream := NewMockQueryService_ExecuteQueryClient(ctrl)
   468  									stream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   469  										if rand.Int31n(100) < 50 {
   470  											txInFlight--
   471  
   472  											return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   473  										}
   474  
   475  										stream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   476  											txInFlight--
   477  
   478  											return nil, io.EOF
   479  										})
   480  
   481  										return &Ydb_Query.ExecuteQueryResponsePart{
   482  											Status: Ydb.StatusIds_SUCCESS,
   483  											TxMeta: &Ydb_Query.TransactionMeta{
   484  												Id: "456",
   485  											},
   486  											ExecStats: &Ydb_TableStats.QueryStats{},
   487  										}, nil
   488  									})
   489  
   490  									return stream, nil
   491  								})
   492  
   493  							return newTestSessionWithClient("123", client, true), nil
   494  						}), func(ctx context.Context, tx query.TxActor) error {
   495  							return tx.Exec(ctx, "", options.WithCommit())
   496  						}, tx.NewSettings(tx.WithSerializableReadWrite()))
   497  						require.NoError(t, err)
   498  						require.Zero(t, txInFlight)
   499  					})
   500  				})
   501  			})
   502  			t.Run("OnSecondExec", func(t *testing.T) {
   503  				t.Run("WithoutCommit", func(t *testing.T) {
   504  					xtest.TestManyTimes(t, func(t testing.TB) {
   505  						ctrl := gomock.NewController(t)
   506  						txInFlight := 0
   507  						err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   508  							client := NewMockQueryServiceClient(ctrl)
   509  							client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   510  								func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   511  									Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   512  								) {
   513  									if rand.Int31n(100) < 50 {
   514  										return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   515  									}
   516  
   517  									txInFlight++
   518  
   519  									firstStream := NewMockQueryService_ExecuteQueryClient(ctrl)
   520  									firstStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   521  										firstStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   522  											client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   523  												func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   524  													Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   525  												) {
   526  													if rand.Int31n(100) < 50 {
   527  														client.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).DoAndReturn(
   528  															func(ctx context.Context,
   529  																request *Ydb_Query.RollbackTransactionRequest,
   530  																option ...grpc.CallOption,
   531  															) (*Ydb_Query.RollbackTransactionResponse, error) {
   532  																txInFlight--
   533  
   534  																return &Ydb_Query.RollbackTransactionResponse{}, nil
   535  															})
   536  
   537  														return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   538  													}
   539  
   540  													secondStream := NewMockQueryService_ExecuteQueryClient(ctrl)
   541  													secondStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   542  														secondStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   543  															client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).DoAndReturn(
   544  																func(ctx context.Context, request *Ydb_Query.CommitTransactionRequest, option ...grpc.CallOption) (
   545  																	*Ydb_Query.CommitTransactionResponse, error,
   546  																) {
   547  																	txInFlight--
   548  
   549  																	return &Ydb_Query.CommitTransactionResponse{
   550  																		Status: Ydb.StatusIds_SUCCESS,
   551  																	}, nil
   552  																})
   553  
   554  															return nil, io.EOF
   555  														})
   556  
   557  														return &Ydb_Query.ExecuteQueryResponsePart{
   558  															Status:    Ydb.StatusIds_SUCCESS,
   559  															TxMeta:    &Ydb_Query.TransactionMeta{},
   560  															ExecStats: &Ydb_TableStats.QueryStats{},
   561  														}, nil
   562  													})
   563  
   564  													return secondStream, nil
   565  												})
   566  
   567  											return nil, io.EOF
   568  										})
   569  
   570  										return &Ydb_Query.ExecuteQueryResponsePart{
   571  											Status: Ydb.StatusIds_SUCCESS,
   572  											TxMeta: &Ydb_Query.TransactionMeta{
   573  												Id: "456",
   574  											},
   575  											ExecStats: &Ydb_TableStats.QueryStats{},
   576  										}, nil
   577  									})
   578  
   579  									return firstStream, nil
   580  								})
   581  
   582  							return newTestSessionWithClient("123", client, true), nil
   583  						}), func(ctx context.Context, tx query.TxActor) error {
   584  							if err := tx.Exec(ctx, ""); err != nil {
   585  								return err
   586  							}
   587  
   588  							return tx.Exec(ctx, "")
   589  						}, tx.NewSettings(tx.WithSerializableReadWrite()))
   590  						require.NoError(t, err)
   591  					})
   592  				})
   593  				t.Run("WithCommit", func(t *testing.T) {
   594  					xtest.TestManyTimes(t, func(t testing.TB) {
   595  						ctrl := gomock.NewController(t)
   596  						txInFlight := 0
   597  						err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   598  							client := NewMockQueryServiceClient(ctrl)
   599  							client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   600  								func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   601  									Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   602  								) {
   603  									if rand.Int31n(100) < 50 {
   604  										return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   605  									}
   606  
   607  									txInFlight++
   608  
   609  									firstStream := NewMockQueryService_ExecuteQueryClient(ctrl)
   610  									firstStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   611  										firstStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   612  											client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).DoAndReturn(
   613  												func(ctx context.Context, request *Ydb_Query.ExecuteQueryRequest, option ...grpc.CallOption) (
   614  													Ydb_Query_V1.QueryService_ExecuteQueryClient, error,
   615  												) {
   616  													require.True(t, request.GetTxControl().GetCommitTx())
   617  
   618  													if rand.Int31n(100) < 50 {
   619  														client.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).DoAndReturn(
   620  															func(ctx context.Context,
   621  																request *Ydb_Query.RollbackTransactionRequest,
   622  																option ...grpc.CallOption,
   623  															) (*Ydb_Query.RollbackTransactionResponse, error) {
   624  																txInFlight--
   625  
   626  																return &Ydb_Query.RollbackTransactionResponse{}, nil
   627  															})
   628  
   629  														return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))
   630  													}
   631  
   632  													secondStream := NewMockQueryService_ExecuteQueryClient(ctrl)
   633  													secondStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   634  														secondStream.EXPECT().Recv().DoAndReturn(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
   635  															return nil, io.EOF
   636  														})
   637  
   638  														return &Ydb_Query.ExecuteQueryResponsePart{
   639  															Status:    Ydb.StatusIds_SUCCESS,
   640  															TxMeta:    &Ydb_Query.TransactionMeta{},
   641  															ExecStats: &Ydb_TableStats.QueryStats{},
   642  														}, nil
   643  													})
   644  
   645  													return secondStream, nil
   646  												})
   647  
   648  											return nil, io.EOF
   649  										})
   650  
   651  										return &Ydb_Query.ExecuteQueryResponsePart{
   652  											Status: Ydb.StatusIds_SUCCESS,
   653  											TxMeta: &Ydb_Query.TransactionMeta{
   654  												Id: "456",
   655  											},
   656  											ExecStats: &Ydb_TableStats.QueryStats{},
   657  										}, nil
   658  									})
   659  
   660  									return firstStream, nil
   661  								})
   662  
   663  							return newTestSessionWithClient("123", client, true), nil
   664  						}), func(ctx context.Context, tx query.TxActor) error {
   665  							if err := tx.Exec(ctx, ""); err != nil {
   666  								return err
   667  							}
   668  
   669  							return tx.Exec(ctx, "", options.WithCommit())
   670  						}, tx.NewSettings(tx.WithSerializableReadWrite()))
   671  						require.NoError(t, err)
   672  					})
   673  				})
   674  			})
   675  		})
   676  	})
   677  	t.Run("Exec", func(t *testing.T) {
   678  		t.Run("HappyWay", func(t *testing.T) {
   679  			ctrl := gomock.NewController(t)
   680  			err := clientExec(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   681  				stream := NewMockQueryService_ExecuteQueryClient(ctrl)
   682  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
   683  					Status: Ydb.StatusIds_SUCCESS,
   684  					TxMeta: &Ydb_Query.TransactionMeta{
   685  						Id: "456",
   686  					},
   687  					ResultSetIndex: 0,
   688  					ResultSet: &Ydb.ResultSet{
   689  						Columns: []*Ydb.Column{
   690  							{
   691  								Name: "a",
   692  								Type: &Ydb.Type{
   693  									Type: &Ydb.Type_TypeId{
   694  										TypeId: Ydb.Type_UINT64,
   695  									},
   696  								},
   697  							},
   698  							{
   699  								Name: "b",
   700  								Type: &Ydb.Type{
   701  									Type: &Ydb.Type_TypeId{
   702  										TypeId: Ydb.Type_UTF8,
   703  									},
   704  								},
   705  							},
   706  						},
   707  						Rows: []*Ydb.Value{
   708  							{
   709  								Items: []*Ydb.Value{{
   710  									Value: &Ydb.Value_Uint64Value{
   711  										Uint64Value: 1,
   712  									},
   713  								}, {
   714  									Value: &Ydb.Value_TextValue{
   715  										TextValue: "1",
   716  									},
   717  								}},
   718  							},
   719  							{
   720  								Items: []*Ydb.Value{{
   721  									Value: &Ydb.Value_Uint64Value{
   722  										Uint64Value: 2,
   723  									},
   724  								}, {
   725  									Value: &Ydb.Value_TextValue{
   726  										TextValue: "2",
   727  									},
   728  								}},
   729  							},
   730  							{
   731  								Items: []*Ydb.Value{{
   732  									Value: &Ydb.Value_Uint64Value{
   733  										Uint64Value: 3,
   734  									},
   735  								}, {
   736  									Value: &Ydb.Value_TextValue{
   737  										TextValue: "3",
   738  									},
   739  								}},
   740  							},
   741  						},
   742  					},
   743  				}, nil)
   744  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
   745  					Status:         Ydb.StatusIds_SUCCESS,
   746  					ResultSetIndex: 0,
   747  					ResultSet: &Ydb.ResultSet{
   748  						Rows: []*Ydb.Value{
   749  							{
   750  								Items: []*Ydb.Value{{
   751  									Value: &Ydb.Value_Uint64Value{
   752  										Uint64Value: 4,
   753  									},
   754  								}, {
   755  									Value: &Ydb.Value_TextValue{
   756  										TextValue: "4",
   757  									},
   758  								}},
   759  							},
   760  							{
   761  								Items: []*Ydb.Value{{
   762  									Value: &Ydb.Value_Uint64Value{
   763  										Uint64Value: 5,
   764  									},
   765  								}, {
   766  									Value: &Ydb.Value_TextValue{
   767  										TextValue: "5",
   768  									},
   769  								}},
   770  							},
   771  						},
   772  					},
   773  				}, nil)
   774  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
   775  					Status:         Ydb.StatusIds_SUCCESS,
   776  					ResultSetIndex: 1,
   777  					ResultSet: &Ydb.ResultSet{
   778  						Columns: []*Ydb.Column{
   779  							{
   780  								Name: "c",
   781  								Type: &Ydb.Type{
   782  									Type: &Ydb.Type_TypeId{
   783  										TypeId: Ydb.Type_UINT64,
   784  									},
   785  								},
   786  							},
   787  							{
   788  								Name: "d",
   789  								Type: &Ydb.Type{
   790  									Type: &Ydb.Type_TypeId{
   791  										TypeId: Ydb.Type_UTF8,
   792  									},
   793  								},
   794  							},
   795  							{
   796  								Name: "e",
   797  								Type: &Ydb.Type{
   798  									Type: &Ydb.Type_TypeId{
   799  										TypeId: Ydb.Type_BOOL,
   800  									},
   801  								},
   802  							},
   803  						},
   804  						Rows: []*Ydb.Value{
   805  							{
   806  								Items: []*Ydb.Value{{
   807  									Value: &Ydb.Value_Uint64Value{
   808  										Uint64Value: 1,
   809  									},
   810  								}, {
   811  									Value: &Ydb.Value_TextValue{
   812  										TextValue: "1",
   813  									},
   814  								}, {
   815  									Value: &Ydb.Value_BoolValue{
   816  										BoolValue: true,
   817  									},
   818  								}},
   819  							},
   820  							{
   821  								Items: []*Ydb.Value{{
   822  									Value: &Ydb.Value_Uint64Value{
   823  										Uint64Value: 2,
   824  									},
   825  								}, {
   826  									Value: &Ydb.Value_TextValue{
   827  										TextValue: "2",
   828  									},
   829  								}, {
   830  									Value: &Ydb.Value_BoolValue{
   831  										BoolValue: false,
   832  									},
   833  								}},
   834  							},
   835  						},
   836  					},
   837  				}, nil)
   838  				stream.EXPECT().Recv().Return(nil, io.EOF)
   839  				client := NewMockQueryServiceClient(ctrl)
   840  				client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
   841  
   842  				return newTestSessionWithClient("123", client, true), nil
   843  			}), "")
   844  			require.NoError(t, err)
   845  		})
   846  	})
   847  	t.Run("Query", func(t *testing.T) {
   848  		t.Run("HappyWay", func(t *testing.T) {
   849  			ctrl := gomock.NewController(t)
   850  			r, err := clientQuery(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
   851  				stream := NewMockQueryService_ExecuteQueryClient(ctrl)
   852  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
   853  					Status: Ydb.StatusIds_SUCCESS,
   854  					TxMeta: &Ydb_Query.TransactionMeta{
   855  						Id: "456",
   856  					},
   857  					ResultSetIndex: 0,
   858  					ResultSet: &Ydb.ResultSet{
   859  						Columns: []*Ydb.Column{
   860  							{
   861  								Name: "a",
   862  								Type: &Ydb.Type{
   863  									Type: &Ydb.Type_TypeId{
   864  										TypeId: Ydb.Type_UINT64,
   865  									},
   866  								},
   867  							},
   868  							{
   869  								Name: "b",
   870  								Type: &Ydb.Type{
   871  									Type: &Ydb.Type_TypeId{
   872  										TypeId: Ydb.Type_UTF8,
   873  									},
   874  								},
   875  							},
   876  						},
   877  						Rows: []*Ydb.Value{
   878  							{
   879  								Items: []*Ydb.Value{{
   880  									Value: &Ydb.Value_Uint64Value{
   881  										Uint64Value: 1,
   882  									},
   883  								}, {
   884  									Value: &Ydb.Value_TextValue{
   885  										TextValue: "1",
   886  									},
   887  								}},
   888  							},
   889  							{
   890  								Items: []*Ydb.Value{{
   891  									Value: &Ydb.Value_Uint64Value{
   892  										Uint64Value: 2,
   893  									},
   894  								}, {
   895  									Value: &Ydb.Value_TextValue{
   896  										TextValue: "2",
   897  									},
   898  								}},
   899  							},
   900  							{
   901  								Items: []*Ydb.Value{{
   902  									Value: &Ydb.Value_Uint64Value{
   903  										Uint64Value: 3,
   904  									},
   905  								}, {
   906  									Value: &Ydb.Value_TextValue{
   907  										TextValue: "3",
   908  									},
   909  								}},
   910  							},
   911  						},
   912  					},
   913  				}, nil)
   914  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
   915  					Status:         Ydb.StatusIds_SUCCESS,
   916  					ResultSetIndex: 0,
   917  					ResultSet: &Ydb.ResultSet{
   918  						Rows: []*Ydb.Value{
   919  							{
   920  								Items: []*Ydb.Value{{
   921  									Value: &Ydb.Value_Uint64Value{
   922  										Uint64Value: 4,
   923  									},
   924  								}, {
   925  									Value: &Ydb.Value_TextValue{
   926  										TextValue: "4",
   927  									},
   928  								}},
   929  							},
   930  							{
   931  								Items: []*Ydb.Value{{
   932  									Value: &Ydb.Value_Uint64Value{
   933  										Uint64Value: 5,
   934  									},
   935  								}, {
   936  									Value: &Ydb.Value_TextValue{
   937  										TextValue: "5",
   938  									},
   939  								}},
   940  							},
   941  						},
   942  					},
   943  				}, nil)
   944  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
   945  					Status:         Ydb.StatusIds_SUCCESS,
   946  					ResultSetIndex: 1,
   947  					ResultSet: &Ydb.ResultSet{
   948  						Columns: []*Ydb.Column{
   949  							{
   950  								Name: "c",
   951  								Type: &Ydb.Type{
   952  									Type: &Ydb.Type_TypeId{
   953  										TypeId: Ydb.Type_UINT64,
   954  									},
   955  								},
   956  							},
   957  							{
   958  								Name: "d",
   959  								Type: &Ydb.Type{
   960  									Type: &Ydb.Type_TypeId{
   961  										TypeId: Ydb.Type_UTF8,
   962  									},
   963  								},
   964  							},
   965  							{
   966  								Name: "e",
   967  								Type: &Ydb.Type{
   968  									Type: &Ydb.Type_TypeId{
   969  										TypeId: Ydb.Type_BOOL,
   970  									},
   971  								},
   972  							},
   973  						},
   974  						Rows: []*Ydb.Value{
   975  							{
   976  								Items: []*Ydb.Value{{
   977  									Value: &Ydb.Value_Uint64Value{
   978  										Uint64Value: 1,
   979  									},
   980  								}, {
   981  									Value: &Ydb.Value_TextValue{
   982  										TextValue: "1",
   983  									},
   984  								}, {
   985  									Value: &Ydb.Value_BoolValue{
   986  										BoolValue: true,
   987  									},
   988  								}},
   989  							},
   990  							{
   991  								Items: []*Ydb.Value{{
   992  									Value: &Ydb.Value_Uint64Value{
   993  										Uint64Value: 2,
   994  									},
   995  								}, {
   996  									Value: &Ydb.Value_TextValue{
   997  										TextValue: "2",
   998  									},
   999  								}, {
  1000  									Value: &Ydb.Value_BoolValue{
  1001  										BoolValue: false,
  1002  									},
  1003  								}},
  1004  							},
  1005  						},
  1006  					},
  1007  				}, nil)
  1008  				stream.EXPECT().Recv().Return(nil, io.EOF)
  1009  				client := NewMockQueryServiceClient(ctrl)
  1010  				client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
  1011  
  1012  				return newTestSessionWithClient("123", client, true), nil
  1013  			}), "")
  1014  			require.NoError(t, err)
  1015  			{
  1016  				rs, err := r.NextResultSet(ctx)
  1017  				require.NoError(t, err)
  1018  				r1, err := rs.NextRow(ctx)
  1019  				require.NoError(t, err)
  1020  				var (
  1021  					a uint64
  1022  					b string
  1023  				)
  1024  				err = r1.Scan(&a, &b)
  1025  				require.NoError(t, err)
  1026  				require.EqualValues(t, 1, a)
  1027  				require.EqualValues(t, "1", b)
  1028  				r2, err := rs.NextRow(ctx)
  1029  				require.NoError(t, err)
  1030  				err = r2.Scan(&a, &b)
  1031  				require.NoError(t, err)
  1032  				require.EqualValues(t, 2, a)
  1033  				require.EqualValues(t, "2", b)
  1034  				r3, err := rs.NextRow(ctx)
  1035  				require.NoError(t, err)
  1036  				err = r3.Scan(&a, &b)
  1037  				require.NoError(t, err)
  1038  				require.EqualValues(t, 3, a)
  1039  				require.EqualValues(t, "3", b)
  1040  				r4, err := rs.NextRow(ctx)
  1041  				require.NoError(t, err)
  1042  				err = r4.Scan(&a, &b)
  1043  				require.NoError(t, err)
  1044  				require.EqualValues(t, 4, a)
  1045  				require.EqualValues(t, "4", b)
  1046  				r5, err := rs.NextRow(ctx)
  1047  				require.NoError(t, err)
  1048  				err = r5.Scan(&a, &b)
  1049  				require.NoError(t, err)
  1050  				require.EqualValues(t, 5, a)
  1051  				require.EqualValues(t, "5", b)
  1052  				r6, err := rs.NextRow(ctx)
  1053  				require.ErrorIs(t, err, io.EOF)
  1054  				require.Nil(t, r6)
  1055  			}
  1056  			{
  1057  				rs, err := r.NextResultSet(ctx)
  1058  				require.NoError(t, err)
  1059  				r1, err := rs.NextRow(ctx)
  1060  				require.NoError(t, err)
  1061  				var (
  1062  					a uint64
  1063  					b string
  1064  					c bool
  1065  				)
  1066  				err = r1.Scan(&a, &b, &c)
  1067  				require.NoError(t, err)
  1068  				require.EqualValues(t, 1, a)
  1069  				require.EqualValues(t, "1", b)
  1070  				require.EqualValues(t, true, c)
  1071  				r2, err := rs.NextRow(ctx)
  1072  				require.NoError(t, err)
  1073  				err = r2.Scan(&a, &b, &c)
  1074  				require.NoError(t, err)
  1075  				require.EqualValues(t, 2, a)
  1076  				require.EqualValues(t, "2", b)
  1077  				require.EqualValues(t, false, c)
  1078  				r3, err := rs.NextRow(ctx)
  1079  				require.ErrorIs(t, err, io.EOF)
  1080  				require.Nil(t, r3)
  1081  			}
  1082  		})
  1083  	})
  1084  	t.Run("QueryResultSet", func(t *testing.T) {
  1085  		t.Run("HappyWay", func(t *testing.T) {
  1086  			ctrl := gomock.NewController(t)
  1087  			rs, err := clientQueryResultSet(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
  1088  				stream := NewMockQueryService_ExecuteQueryClient(ctrl)
  1089  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1090  					Status: Ydb.StatusIds_SUCCESS,
  1091  					TxMeta: &Ydb_Query.TransactionMeta{
  1092  						Id: "456",
  1093  					},
  1094  					ResultSetIndex: 0,
  1095  					ResultSet: &Ydb.ResultSet{
  1096  						Columns: []*Ydb.Column{
  1097  							{
  1098  								Name: "a",
  1099  								Type: &Ydb.Type{
  1100  									Type: &Ydb.Type_TypeId{
  1101  										TypeId: Ydb.Type_UINT64,
  1102  									},
  1103  								},
  1104  							},
  1105  							{
  1106  								Name: "b",
  1107  								Type: &Ydb.Type{
  1108  									Type: &Ydb.Type_TypeId{
  1109  										TypeId: Ydb.Type_UTF8,
  1110  									},
  1111  								},
  1112  							},
  1113  						},
  1114  						Rows: []*Ydb.Value{
  1115  							{
  1116  								Items: []*Ydb.Value{{
  1117  									Value: &Ydb.Value_Uint64Value{
  1118  										Uint64Value: 1,
  1119  									},
  1120  								}, {
  1121  									Value: &Ydb.Value_TextValue{
  1122  										TextValue: "1",
  1123  									},
  1124  								}},
  1125  							},
  1126  							{
  1127  								Items: []*Ydb.Value{{
  1128  									Value: &Ydb.Value_Uint64Value{
  1129  										Uint64Value: 2,
  1130  									},
  1131  								}, {
  1132  									Value: &Ydb.Value_TextValue{
  1133  										TextValue: "2",
  1134  									},
  1135  								}},
  1136  							},
  1137  							{
  1138  								Items: []*Ydb.Value{{
  1139  									Value: &Ydb.Value_Uint64Value{
  1140  										Uint64Value: 3,
  1141  									},
  1142  								}, {
  1143  									Value: &Ydb.Value_TextValue{
  1144  										TextValue: "3",
  1145  									},
  1146  								}},
  1147  							},
  1148  						},
  1149  					},
  1150  				}, nil)
  1151  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1152  					Status:         Ydb.StatusIds_SUCCESS,
  1153  					ResultSetIndex: 0,
  1154  					ResultSet: &Ydb.ResultSet{
  1155  						Rows: []*Ydb.Value{
  1156  							{
  1157  								Items: []*Ydb.Value{{
  1158  									Value: &Ydb.Value_Uint64Value{
  1159  										Uint64Value: 4,
  1160  									},
  1161  								}, {
  1162  									Value: &Ydb.Value_TextValue{
  1163  										TextValue: "4",
  1164  									},
  1165  								}},
  1166  							},
  1167  							{
  1168  								Items: []*Ydb.Value{{
  1169  									Value: &Ydb.Value_Uint64Value{
  1170  										Uint64Value: 5,
  1171  									},
  1172  								}, {
  1173  									Value: &Ydb.Value_TextValue{
  1174  										TextValue: "5",
  1175  									},
  1176  								}},
  1177  							},
  1178  						},
  1179  					},
  1180  				}, nil)
  1181  				stream.EXPECT().Recv().Return(nil, io.EOF)
  1182  				client := NewMockQueryServiceClient(ctrl)
  1183  				client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
  1184  
  1185  				return newTestSessionWithClient("123", client, true), nil
  1186  			}), "", options.ExecuteSettings())
  1187  			require.NoError(t, err)
  1188  			require.NotNil(t, rs)
  1189  			{
  1190  				require.NoError(t, err)
  1191  				r1, err := rs.NextRow(ctx)
  1192  				require.NoError(t, err)
  1193  				var (
  1194  					a uint64
  1195  					b string
  1196  				)
  1197  				err = r1.Scan(&a, &b)
  1198  				require.NoError(t, err)
  1199  				require.EqualValues(t, 1, a)
  1200  				require.EqualValues(t, "1", b)
  1201  				r2, err := rs.NextRow(ctx)
  1202  				require.NoError(t, err)
  1203  				err = r2.Scan(&a, &b)
  1204  				require.NoError(t, err)
  1205  				require.EqualValues(t, 2, a)
  1206  				require.EqualValues(t, "2", b)
  1207  				r3, err := rs.NextRow(ctx)
  1208  				require.NoError(t, err)
  1209  				err = r3.Scan(&a, &b)
  1210  				require.NoError(t, err)
  1211  				require.EqualValues(t, 3, a)
  1212  				require.EqualValues(t, "3", b)
  1213  				r4, err := rs.NextRow(ctx)
  1214  				require.NoError(t, err)
  1215  				err = r4.Scan(&a, &b)
  1216  				require.NoError(t, err)
  1217  				require.EqualValues(t, 4, a)
  1218  				require.EqualValues(t, "4", b)
  1219  				r5, err := rs.NextRow(ctx)
  1220  				require.NoError(t, err)
  1221  				err = r5.Scan(&a, &b)
  1222  				require.NoError(t, err)
  1223  				require.EqualValues(t, 5, a)
  1224  				require.EqualValues(t, "5", b)
  1225  				r6, err := rs.NextRow(ctx)
  1226  				require.ErrorIs(t, err, io.EOF)
  1227  				require.Nil(t, r6)
  1228  			}
  1229  		})
  1230  		t.Run("MoreThanOneResultSet", func(t *testing.T) {
  1231  			ctrl := gomock.NewController(t)
  1232  			rs, err := clientQueryResultSet(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
  1233  				stream := NewMockQueryService_ExecuteQueryClient(ctrl)
  1234  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1235  					Status: Ydb.StatusIds_SUCCESS,
  1236  					TxMeta: &Ydb_Query.TransactionMeta{
  1237  						Id: "456",
  1238  					},
  1239  					ResultSetIndex: 0,
  1240  					ResultSet: &Ydb.ResultSet{
  1241  						Columns: []*Ydb.Column{
  1242  							{
  1243  								Name: "a",
  1244  								Type: &Ydb.Type{
  1245  									Type: &Ydb.Type_TypeId{
  1246  										TypeId: Ydb.Type_UINT64,
  1247  									},
  1248  								},
  1249  							},
  1250  							{
  1251  								Name: "b",
  1252  								Type: &Ydb.Type{
  1253  									Type: &Ydb.Type_TypeId{
  1254  										TypeId: Ydb.Type_UTF8,
  1255  									},
  1256  								},
  1257  							},
  1258  						},
  1259  						Rows: []*Ydb.Value{
  1260  							{
  1261  								Items: []*Ydb.Value{{
  1262  									Value: &Ydb.Value_Uint64Value{
  1263  										Uint64Value: 1,
  1264  									},
  1265  								}, {
  1266  									Value: &Ydb.Value_TextValue{
  1267  										TextValue: "1",
  1268  									},
  1269  								}},
  1270  							},
  1271  							{
  1272  								Items: []*Ydb.Value{{
  1273  									Value: &Ydb.Value_Uint64Value{
  1274  										Uint64Value: 2,
  1275  									},
  1276  								}, {
  1277  									Value: &Ydb.Value_TextValue{
  1278  										TextValue: "2",
  1279  									},
  1280  								}},
  1281  							},
  1282  							{
  1283  								Items: []*Ydb.Value{{
  1284  									Value: &Ydb.Value_Uint64Value{
  1285  										Uint64Value: 3,
  1286  									},
  1287  								}, {
  1288  									Value: &Ydb.Value_TextValue{
  1289  										TextValue: "3",
  1290  									},
  1291  								}},
  1292  							},
  1293  						},
  1294  					},
  1295  				}, nil)
  1296  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1297  					Status:         Ydb.StatusIds_SUCCESS,
  1298  					ResultSetIndex: 0,
  1299  					ResultSet: &Ydb.ResultSet{
  1300  						Rows: []*Ydb.Value{
  1301  							{
  1302  								Items: []*Ydb.Value{{
  1303  									Value: &Ydb.Value_Uint64Value{
  1304  										Uint64Value: 4,
  1305  									},
  1306  								}, {
  1307  									Value: &Ydb.Value_TextValue{
  1308  										TextValue: "4",
  1309  									},
  1310  								}},
  1311  							},
  1312  							{
  1313  								Items: []*Ydb.Value{{
  1314  									Value: &Ydb.Value_Uint64Value{
  1315  										Uint64Value: 5,
  1316  									},
  1317  								}, {
  1318  									Value: &Ydb.Value_TextValue{
  1319  										TextValue: "5",
  1320  									},
  1321  								}},
  1322  							},
  1323  						},
  1324  					},
  1325  				}, nil)
  1326  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1327  					Status:         Ydb.StatusIds_SUCCESS,
  1328  					ResultSetIndex: 1,
  1329  					ResultSet: &Ydb.ResultSet{
  1330  						Columns: []*Ydb.Column{
  1331  							{
  1332  								Name: "c",
  1333  								Type: &Ydb.Type{
  1334  									Type: &Ydb.Type_TypeId{
  1335  										TypeId: Ydb.Type_UINT64,
  1336  									},
  1337  								},
  1338  							},
  1339  							{
  1340  								Name: "d",
  1341  								Type: &Ydb.Type{
  1342  									Type: &Ydb.Type_TypeId{
  1343  										TypeId: Ydb.Type_UTF8,
  1344  									},
  1345  								},
  1346  							},
  1347  							{
  1348  								Name: "e",
  1349  								Type: &Ydb.Type{
  1350  									Type: &Ydb.Type_TypeId{
  1351  										TypeId: Ydb.Type_BOOL,
  1352  									},
  1353  								},
  1354  							},
  1355  						},
  1356  						Rows: []*Ydb.Value{
  1357  							{
  1358  								Items: []*Ydb.Value{{
  1359  									Value: &Ydb.Value_Uint64Value{
  1360  										Uint64Value: 1,
  1361  									},
  1362  								}, {
  1363  									Value: &Ydb.Value_TextValue{
  1364  										TextValue: "1",
  1365  									},
  1366  								}, {
  1367  									Value: &Ydb.Value_BoolValue{
  1368  										BoolValue: true,
  1369  									},
  1370  								}},
  1371  							},
  1372  							{
  1373  								Items: []*Ydb.Value{{
  1374  									Value: &Ydb.Value_Uint64Value{
  1375  										Uint64Value: 2,
  1376  									},
  1377  								}, {
  1378  									Value: &Ydb.Value_TextValue{
  1379  										TextValue: "2",
  1380  									},
  1381  								}, {
  1382  									Value: &Ydb.Value_BoolValue{
  1383  										BoolValue: false,
  1384  									},
  1385  								}},
  1386  							},
  1387  						},
  1388  					},
  1389  				}, nil)
  1390  				stream.EXPECT().Recv().Return(nil, io.EOF)
  1391  				client := NewMockQueryServiceClient(ctrl)
  1392  				client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
  1393  
  1394  				return newTestSessionWithClient("123", client, true), nil
  1395  			}), "", options.ExecuteSettings())
  1396  			require.ErrorIs(t, err, errMoreThanOneResultSet)
  1397  			require.Nil(t, rs)
  1398  		})
  1399  	})
  1400  	t.Run("QueryRow", func(t *testing.T) {
  1401  		t.Run("HappyWay", func(t *testing.T) {
  1402  			ctrl := gomock.NewController(t)
  1403  			row, err := clientQueryRow(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
  1404  				stream := NewMockQueryService_ExecuteQueryClient(ctrl)
  1405  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1406  					Status: Ydb.StatusIds_SUCCESS,
  1407  					TxMeta: &Ydb_Query.TransactionMeta{
  1408  						Id: "456",
  1409  					},
  1410  					ResultSetIndex: 0,
  1411  					ResultSet: &Ydb.ResultSet{
  1412  						Columns: []*Ydb.Column{
  1413  							{
  1414  								Name: "a",
  1415  								Type: &Ydb.Type{
  1416  									Type: &Ydb.Type_TypeId{
  1417  										TypeId: Ydb.Type_UINT64,
  1418  									},
  1419  								},
  1420  							},
  1421  							{
  1422  								Name: "b",
  1423  								Type: &Ydb.Type{
  1424  									Type: &Ydb.Type_TypeId{
  1425  										TypeId: Ydb.Type_UTF8,
  1426  									},
  1427  								},
  1428  							},
  1429  						},
  1430  						Rows: []*Ydb.Value{
  1431  							{
  1432  								Items: []*Ydb.Value{{
  1433  									Value: &Ydb.Value_Uint64Value{
  1434  										Uint64Value: 1,
  1435  									},
  1436  								}, {
  1437  									Value: &Ydb.Value_TextValue{
  1438  										TextValue: "1",
  1439  									},
  1440  								}},
  1441  							},
  1442  						},
  1443  					},
  1444  				}, nil)
  1445  				stream.EXPECT().Recv().Return(nil, io.EOF)
  1446  				client := NewMockQueryServiceClient(ctrl)
  1447  				client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
  1448  
  1449  				return newTestSessionWithClient("123", client, true), nil
  1450  			}), "", options.ExecuteSettings())
  1451  			require.NoError(t, err)
  1452  			require.NotNil(t, row)
  1453  			{
  1454  				var (
  1455  					a uint64
  1456  					b string
  1457  				)
  1458  				err = row.Scan(&a, &b)
  1459  				require.NoError(t, err)
  1460  				require.EqualValues(t, 1, a)
  1461  				require.EqualValues(t, "1", b)
  1462  			}
  1463  		})
  1464  		t.Run("MoreThanOneRow", func(t *testing.T) {
  1465  			ctrl := gomock.NewController(t)
  1466  			row, err := clientQueryRow(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
  1467  				stream := NewMockQueryService_ExecuteQueryClient(ctrl)
  1468  				stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
  1469  					Status: Ydb.StatusIds_SUCCESS,
  1470  					TxMeta: &Ydb_Query.TransactionMeta{
  1471  						Id: "456",
  1472  					},
  1473  					ResultSetIndex: 0,
  1474  					ResultSet: &Ydb.ResultSet{
  1475  						Columns: []*Ydb.Column{
  1476  							{
  1477  								Name: "a",
  1478  								Type: &Ydb.Type{
  1479  									Type: &Ydb.Type_TypeId{
  1480  										TypeId: Ydb.Type_UINT64,
  1481  									},
  1482  								},
  1483  							},
  1484  							{
  1485  								Name: "b",
  1486  								Type: &Ydb.Type{
  1487  									Type: &Ydb.Type_TypeId{
  1488  										TypeId: Ydb.Type_UTF8,
  1489  									},
  1490  								},
  1491  							},
  1492  						},
  1493  						Rows: []*Ydb.Value{
  1494  							{
  1495  								Items: []*Ydb.Value{{
  1496  									Value: &Ydb.Value_Uint64Value{
  1497  										Uint64Value: 1,
  1498  									},
  1499  								}, {
  1500  									Value: &Ydb.Value_TextValue{
  1501  										TextValue: "1",
  1502  									},
  1503  								}},
  1504  							},
  1505  							{
  1506  								Items: []*Ydb.Value{{
  1507  									Value: &Ydb.Value_Uint64Value{
  1508  										Uint64Value: 2,
  1509  									},
  1510  								}, {
  1511  									Value: &Ydb.Value_TextValue{
  1512  										TextValue: "2",
  1513  									},
  1514  								}},
  1515  							},
  1516  							{
  1517  								Items: []*Ydb.Value{{
  1518  									Value: &Ydb.Value_Uint64Value{
  1519  										Uint64Value: 3,
  1520  									},
  1521  								}, {
  1522  									Value: &Ydb.Value_TextValue{
  1523  										TextValue: "3",
  1524  									},
  1525  								}},
  1526  							},
  1527  						},
  1528  					},
  1529  				}, nil)
  1530  				stream.EXPECT().Recv().Return(nil, io.EOF)
  1531  				client := NewMockQueryServiceClient(ctrl)
  1532  				client.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
  1533  
  1534  				return newTestSessionWithClient("123", client, true), nil
  1535  			}), "", options.ExecuteSettings())
  1536  			require.ErrorIs(t, err, errMoreThanOneRow)
  1537  			require.Nil(t, row)
  1538  		})
  1539  	})
  1540  }
  1541  
  1542  type sessionControllerMock struct {
  1543  	id     string
  1544  	status session.Status
  1545  }
  1546  
  1547  func (s *sessionControllerMock) IsAlive() bool {
  1548  	return session.IsAlive(s.status)
  1549  }
  1550  
  1551  func (s *sessionControllerMock) Close(ctx context.Context) error {
  1552  	return nil
  1553  }
  1554  
  1555  func (s *sessionControllerMock) SetStatus(status session.Status) {
  1556  	s.status = status
  1557  }
  1558  
  1559  func (s *sessionControllerMock) ID() string {
  1560  	return s.id
  1561  }
  1562  
  1563  func (s *sessionControllerMock) NodeID() uint32 {
  1564  	return 0
  1565  }
  1566  
  1567  func (s sessionControllerMock) Status() string {
  1568  	return s.status.String()
  1569  }
  1570  
  1571  func newTestSession(id string) *Session {
  1572  	return &Session{
  1573  		Core:  &sessionControllerMock{id: id},
  1574  		trace: &trace.Query{},
  1575  	}
  1576  }
  1577  
  1578  func newTestSessionWithClient(id string, client Ydb_Query_V1.QueryServiceClient, lazyTx bool) *Session {
  1579  	return &Session{
  1580  		Core:   &sessionControllerMock{id: id},
  1581  		client: client,
  1582  		trace:  &trace.Query{},
  1583  		laztTx: lazyTx,
  1584  	}
  1585  }
  1586  
  1587  func testPool(
  1588  	ctx context.Context,
  1589  	createSession func(ctx context.Context) (*Session, error),
  1590  ) *pool.Pool[*Session, Session] {
  1591  	return pool.New[*Session, Session](ctx,
  1592  		pool.WithLimit[*Session, Session](1),
  1593  		pool.WithCreateItemFunc(createSession),
  1594  		pool.WithSyncCloseItem[*Session, Session](),
  1595  	)
  1596  }
  1597  
  1598  func TestQueryScript(t *testing.T) {
  1599  	ctx := xtest.Context(t)
  1600  	t.Run("HappyWay", func(t *testing.T) {
  1601  		ctrl := gomock.NewController(t)
  1602  		client := NewMockQueryServiceClient(ctrl)
  1603  		client.EXPECT().ExecuteScript(gomock.Any(), gomock.Any()).Return(&Ydb_Operations.Operation{
  1604  			Id:     "123",
  1605  			Ready:  true,
  1606  			Status: Ydb.StatusIds_SUCCESS,
  1607  			Metadata: xtest.Must(anypb.New(&Ydb_Query.ExecuteScriptMetadata{
  1608  				ExecutionId: "123",
  1609  				ExecStatus:  Ydb_Query.ExecStatus_EXEC_STATUS_STARTING,
  1610  				ScriptContent: &Ydb_Query.QueryContent{
  1611  					Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
  1612  					Text:   "SELECT 1 AS a, 2 AS b",
  1613  				},
  1614  				ResultSetsMeta: []*Ydb_Query.ResultSetMeta{
  1615  					{
  1616  						Columns: []*Ydb.Column{
  1617  							{
  1618  								Name: "a",
  1619  								Type: &Ydb.Type{
  1620  									Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
  1621  								},
  1622  							},
  1623  							{
  1624  								Name: "b",
  1625  								Type: &Ydb.Type{
  1626  									Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
  1627  								},
  1628  							},
  1629  						},
  1630  					},
  1631  				},
  1632  				ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
  1633  				ExecStats: &Ydb_TableStats.QueryStats{
  1634  					QueryPhases:      nil,
  1635  					Compilation:      nil,
  1636  					ProcessCpuTimeUs: 0,
  1637  					QueryPlan:        "",
  1638  					QueryAst:         "",
  1639  					TotalDurationUs:  0,
  1640  					TotalCpuTimeUs:   0,
  1641  				},
  1642  			})),
  1643  			CostInfo: nil,
  1644  		}, nil)
  1645  		client.EXPECT().FetchScriptResults(gomock.Any(), gomock.Any()).Return(&Ydb_Query.FetchScriptResultsResponse{
  1646  			Status:         Ydb.StatusIds_SUCCESS,
  1647  			ResultSetIndex: 0,
  1648  			ResultSet: &Ydb.ResultSet{
  1649  				Columns: []*Ydb.Column{
  1650  					{
  1651  						Name: "a",
  1652  						Type: &Ydb.Type{
  1653  							Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
  1654  						},
  1655  					},
  1656  					{
  1657  						Name: "b",
  1658  						Type: &Ydb.Type{
  1659  							Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
  1660  						},
  1661  					},
  1662  				},
  1663  				Rows: []*Ydb.Value{
  1664  					{
  1665  						Items: []*Ydb.Value{
  1666  							{
  1667  								Value: &Ydb.Value_Int32Value{
  1668  									Int32Value: 1,
  1669  								},
  1670  								VariantIndex: 0,
  1671  							},
  1672  							{
  1673  								Value: &Ydb.Value_Int32Value{
  1674  									Int32Value: 2,
  1675  								},
  1676  								VariantIndex: 0,
  1677  							},
  1678  						},
  1679  					},
  1680  				},
  1681  				Truncated: false,
  1682  			},
  1683  			NextFetchToken: "456",
  1684  		}, nil)
  1685  		op, err := executeScript(ctx, client, &Ydb_Query.ExecuteScriptRequest{})
  1686  		require.NoError(t, err)
  1687  		require.EqualValues(t, "123", op.ID)
  1688  		r, err := fetchScriptResults(ctx, client, op.ID)
  1689  		require.NoError(t, err)
  1690  		require.EqualValues(t, 0, r.ResultSetIndex)
  1691  		require.Equal(t, "456", r.NextToken)
  1692  		require.NotNil(t, r.ResultSet)
  1693  		row, err := r.ResultSet.NextRow(ctx)
  1694  		require.NoError(t, err)
  1695  		var (
  1696  			a int
  1697  			b int
  1698  		)
  1699  		err = row.Scan(&a, &b)
  1700  		require.NoError(t, err)
  1701  		require.EqualValues(t, 1, a)
  1702  		require.EqualValues(t, 2, b)
  1703  	})
  1704  	t.Run("Error", func(t *testing.T) {
  1705  		t.Run("OnExecute", func(t *testing.T) {
  1706  		})
  1707  		t.Run("OnFetch", func(t *testing.T) {
  1708  		})
  1709  	})
  1710  }