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 }