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