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