diff --git a/.github/workflows/breaking.yml b/.github/workflows/breaking.yml
index 1670c6f64..17eeb19c3 100644
--- a/.github/workflows/breaking.yml
+++ b/.github/workflows/breaking.yml
@@ -11,6 +11,9 @@ jobs:
group: broken-changes-${{ github.ref }}
cancel-in-progress: true
runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ contents: read
steps:
- name: Install Go
uses: actions/setup-go@v3
@@ -22,6 +25,8 @@ jobs:
run: test -e ~/go/bin/gorelease || go install golang.org/x/exp/cmd/gorelease@latest
- name: Check broken API changes
run: gorelease -base=$GITHUB_BASE_REF 2>&1 > changes.txt | true
+ - name: Print API changes
+ run: cat changes.txt
- name: Comment Report
if: always()
uses: marocchino/sticky-pull-request-comment@v2
diff --git a/.github/workflows/check-codegen.yml b/.github/workflows/check-codegen.yml
index 6ef0f1ed1..0ee6bd406 100644
--- a/.github/workflows/check-codegen.yml
+++ b/.github/workflows/check-codegen.yml
@@ -32,6 +32,7 @@ jobs:
- name: Build
run: |
go install ./internal/cmd/gtrace
+ go install ./internal/cmd/gstack
go install go.uber.org/mock/mockgen@v0.4.0
- name: Clean and re-generate *_gtrace.go files
@@ -40,5 +41,9 @@ jobs:
go generate ./trace
go generate ./...
+ - name: Re-generate stack.FunctionID calls
+ run: |
+ gstack .
+
- name: Check repository diff
run: bash ./.github/scripts/check-work-copy-equals-to-committed.sh "code-generation not equal with committed"
diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index 39fc2f4d7..745864a37 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -15,11 +15,11 @@ jobs:
strategy:
fail-fast: false
matrix:
- ydb-version: [ 22.5, 23.1, 23.2 ]
- application: [ native, database_sql, gorm, xorm ]
+ ydb-version: [ 23.3, 24.1 ]
+ application: [ native/table, native/query, database_sql, gorm, xorm ]
services:
ydb:
- image: cr.yandex/crpsjg1coh47p81vh2lc/yandex-docker-local-ydb:${{ matrix.ydb-version }}
+ image: ydbplatform/local-ydb:${{ matrix.ydb-version }}
ports:
- 2135:2135
- 2136:2136
@@ -29,11 +29,13 @@ jobs:
env:
YDB_LOCAL_SURVIVE_RESTART: true
YDB_USE_IN_MEMORY_PDISKS: true
+ YDB_TABLE_ENABLE_PREPARED_DDL: true
options: '-h localhost'
env:
OS: ubuntu-latest
YDB_CONNECTION_STRING: grpc://localhost:2136/local
YDB_ANONYMOUS_CREDENTIALS: 1
+ YDB_VERSION: ${{ matrix.ydb-version }}
steps:
- name: Checkout code
uses: actions/checkout@v3
diff --git a/.github/workflows/slo.yml b/.github/workflows/slo.yml
index f60064935..d1922303e 100644
--- a/.github/workflows/slo.yml
+++ b/.github/workflows/slo.yml
@@ -43,29 +43,35 @@ jobs:
timeBetweenPhases: 30
shutdownTime: 30
- language_id0: 'native'
+ language_id0: 'native-table'
workload_path0: 'tests/slo'
- language0: 'Go SDK native'
+ language0: 'Native ydb-go-sdk/v3 over table-service'
workload_build_context0: ../..
- workload_build_options0: -f Dockerfile --build-arg SRC_PATH=native --build-arg JOB_NAME=workload-native
+ workload_build_options0: -f Dockerfile --build-arg SRC_PATH=native/table --build-arg JOB_NAME=workload-native-table
- language_id1: 'databasesql'
+ language_id1: 'native-query'
workload_path1: 'tests/slo'
- language1: 'Go SDK database/sql'
+ language1: 'Native ydb-go-sdk/v3 over query-service'
workload_build_context1: ../..
- workload_build_options1: -f Dockerfile --build-arg SRC_PATH=database/sql --build-arg JOB_NAME=workload-databasesql
+ workload_build_options1: -f Dockerfile --build-arg SRC_PATH=native/query --build-arg JOB_NAME=workload-native-query
- language_id2: 'gorm'
+ language_id2: 'database-sql'
workload_path2: 'tests/slo'
- language2: 'Go SDK gorm'
+ language2: 'Go SDK database/sql'
workload_build_context2: ../..
- workload_build_options2: -f Dockerfile --build-arg SRC_PATH=gorm --build-arg JOB_NAME=workload-gorm
+ workload_build_options2: -f Dockerfile --build-arg SRC_PATH=database/sql --build-arg JOB_NAME=workload-database-sql
- language_id3: 'xorm'
+ language_id3: 'gorm'
workload_path3: 'tests/slo'
- language3: 'Go SDK xorm'
+ language3: 'Go SDK gorm'
workload_build_context3: ../..
- workload_build_options3: -f Dockerfile --build-arg SRC_PATH=xorm --build-arg JOB_NAME=workload-xorm
+ workload_build_options3: -f Dockerfile --build-arg SRC_PATH=gorm --build-arg JOB_NAME=workload-gorm
+
+ language_id4: 'xorm'
+ workload_path4: 'tests/slo'
+ language4: 'Go SDK xorm'
+ workload_build_context4: ../..
+ workload_build_options4: -f Dockerfile --build-arg SRC_PATH=xorm --build-arg JOB_NAME=workload-xorm
- uses: actions/upload-artifact@v3
if: always()
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 11ec91715..756b2fd38 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -45,10 +45,10 @@ jobs:
fail-fast: false
matrix:
go-version: [1.21.x, 1.22.x]
- ydb-version: [22.5, 23.1, 23.2, 23.3, 24.1]
+ ydb-version: [23.3, 24.1]
services:
ydb:
- image: cr.yandex/yc/yandex-docker-local-ydb:${{ matrix.ydb-version }}
+ image: ydbplatform/local-ydb:${{ matrix.ydb-version }}
ports:
- 2135:2135
- 2136:2136
@@ -58,6 +58,7 @@ jobs:
env:
YDB_LOCAL_SURVIVE_RESTART: true
YDB_USE_IN_MEMORY_PDISKS: true
+ YDB_TABLE_ENABLE_PREPARED_DDL: true
options: '-h localhost'
env:
OS: ubuntu-latest
diff --git a/.golangci.yml b/.golangci.yml
index c9a37ffb2..f14d77b30 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -223,7 +223,6 @@ linters:
- cyclop
- depguard
- dupl
- - errname
- exhaustive
- exhaustivestruct
- exhaustruct
@@ -239,10 +238,8 @@ linters:
- interfacebloat
- ireturn
- maintidx
- - maligned
- nonamedreturns
- paralleltest
- - protogetter
- scopelint
- structcheck
- testableexamples
@@ -297,6 +294,7 @@ issues:
- predeclared
- path: _test\.go
linters:
+ - funlen
- unused
- unparam
- gocritic
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8cc8d8da0..b6746f299 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,99 @@
+* Restored `WithSessionPoolKeepAliveMinSize` and `WithSessionPoolKeepAliveTimeout` for backward compatibility.
+* Fixed leak timers
+* Changed default StartTime (time of retries for connect to server) for topic writer from 1 minute to infinite (can be overrided by WithWriterStartTimeout topic option)
+* Added `Struct` support for `Variant` in `ydb.ParamsBuilder()`
+* Added `go` with anonymous function case in `gstack`
+
+## v3.61.2
+* Changed default transaction control to `NoTx` for execute query through query service client
+
+## v3.61.1
+* Renamed `db.Coordination().CreateSession()` to `db.Coordination().Session()` for compatibility with protos
+
+## v3.61.0
+* Added `Tuple` support for `Variant` in `ydb.ParamsBuilder()`
+
+## v3.60.1
+* Added additional traces for coordination service client internals
+
+## v3.60.0
+* Added experimental support of semaphores over coordination service client
+
+## v3.59.3
+* Fixed `gstack` logic for parsing `ast.BlockStmt`
+
+## v3.59.2
+* Added internal `gstack` codegen tool for filling `stack.FunctionID` with value from call stack
+
+## v3.59.1
+* Fixed updating last usage timestamp for smart parking of the conns
+
+## v3.59.0
+* Added `Struct` support for `ydb.ParamsBuilder()`
+* Added support of `TzDate`,`TzDateTime`,`TzTimestamp` types in `ydb.ParamsBuilder()`
+* Added `trace.Query.OnTransactionExecute` event
+* Added query pool metrics
+* Fixed logic of query session pool
+* Changed initialization of internal driver clients to lazy
+* Removed `ydb.WithSessionPoolSizeLimit()` option
+* Added async put session into pool if external context is done
+* Dropped intermediate callbacks from `trace.{Table,Retry,Query}` events
+* Wrapped errors from `internal/pool.Pool.getItem` as retryable
+* Disabled the logic of background grpc-connection parking
+* Improved stringification for postgres types
+
+## v3.58.2
+* Added `trace.Query.OnSessionBegin` event
+* Added `trace.Query.OnResult{New,NextPart,NextResultSet,Close}` events
+* Added `trace.Query.OnRow{Scan,ScanNamed,ScanStruct}` events
+
+## v3.58.1
+* Dropped all deprecated callbacks and events from traces
+* Added `trace.Driver.OnConnStream{SendMsg,RecvMsg,CloseSend}` events
+* Added `trace.Query.OnSessionExecute` event
+
+## v3.58.0
+* Changed `List` constructor from `ydb.ParamsBuilder().List().Build().Build()` to `ydb.ParamsBuilder().BeginList().EndList().Build()`
+* Changed `Set` constructor from `ydb.ParamsBuilder().Set().Build().Build()` to `ydb.ParamsBuilder().BeginSet().EndSet().Build()`
+* Changed `Dict` constructor from `ydb.ParamsBuilder().Dict().Build().Build()` to `ydb.ParamsBuilder().BeginDict().EndDict().Build()`
+* Changed `Optional` constructor from `ydb.ParamsBuilder().Set().Build().Build()` to `ydb.ParamsBuilder().BeginOptional().EndOptional().Build()`
+* Added events into `trace.Query` trace
+* Rewrote `internal/pool` to buffered channel
+* Added `internal/xcontext.WithDone()`
+* Added `internal/xsync.{OnceFunc,OnceValue}`
+* Updated `google.golang.org/protobuf` from `v1.31.0` to `v.33.0`
+* Added `ydb.ParamsBuilder().Pg().{Value,Int4,Int8,Unknown}` for postgres arguments
+* Added `Tuple` support for `ydb.ParamsBuilder()`
+
+## v3.57.4
+* Added client pid to each gRPC requests to YDB over header `x-ydb-client-pid`
+* Added `ydb.WithApplicationName` option
+* Added `Dict` support for `ydb.ParamsBuilder()`
+
+## v3.57.3
+* Added metrics over query service internals
+* Added session create and delete events into `trace.Query`
+* Moved public type `query.SessionStatus` into `internal/query` package
+
+## v3.57.2
+* Fixed cases when some option is nil
+
+## v3.57.1
+* Added logs over query service internals
+* Changed `trace.Query` events
+* Changed visibility of `query.{Do,DoTx}Options` from public to private
+
+## v3.57.0
+* Added experimental implementation of query service client
+* Fixed sometime panic on topic writer closing
+* Added experimental query parameters builder `ydb.ParamsBuilder()`
+* Changed types of `table/table.{QueryParameters,ParameterOption}` to aliases on `internal/params.{Parameters,NamedValue}`
+* Fixed bug with optional decimal serialization
+
+## v3.56.2
+* Fixed return private error for commit to stopped partition in topic reader.
+* Stopped wrapping err error as transport error at topic streams (internals)
+
## v3.56.1
* Fixed fixenv usage (related to tests only)
@@ -32,14 +128,14 @@
## v3.54.2
* Added context to some internal methods for better tracing
-* Added `trace.FunctionID` helper and `FunctionID` field to trace start info's
+* Added `trace.FunctionID` helper and `FunctionID` field to trace start info's
* Replaced lazy initialization of ydb clients (table, topic, etc.) to explicit initialization on `ydb.Open` step
## v3.54.1
-* Fixed inconsistent labels in `metrics`
+* Fixed inconsistent labels in `metrics`
## v3.54.0
-* Allowed `sql.LevelSerializable` isolation level in read-write mode in `database/sql` transactions
+* Allowed `sql.LevelSerializable` isolation level in read-write mode in `database/sql` transactions
* Refactored traces and metrics
* Added `{retry,table}.WithLabel` options for mark retriers calls
* Added `ydb.WithTraceRetry` option
@@ -63,7 +159,7 @@
* Fixed stringification of credentials object
## v3.53.2
-* Fixed panic when try to unwrap values with more than 127 columns with custom ydb unmarshaler
+* Fixed panic when try to unwrap values with more than 127 columns with custom ydb unmarshaler
## v3.53.1
* Bumps `github.com/ydb-platform/ydb-go-genproto` for support `query` service
@@ -212,7 +308,7 @@
* Added `table/options.WithCallOptions` options for append custom grpc call options into `session.{BulkUpsert,Execute,StreamExecuteScanQuery}`
* Supported fake transactions in `database/sql` driver over connector option `ydb.WithFakeTx(queryMode)` and connection string param `go_fake_tx`
* Removed `testutil/timeutil` package (all usages replaced with `clockwork` package)
-* Changed behaviour of retryer on transport errors `cancelled` and `deadline exceeded` - will retry idempotent operation if context is not done
+* Changed behaviour of retryer on transport errors `cancelled` and `deadline exceeded` - will retry idempotent operation if context is not done
* Added address of node to operation error description as optional
* Fixed bug with put session from unknown node
* Fixed bug with parsing of `TzTimestamp` without microseconds
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 53e4c6849..3760cd632 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -56,7 +56,7 @@ go test -race -tags fast ./...
##### All tests
```sh
-docker run -itd --name ydb -dp 2135:2135 -dp 2136:2136 -dp 8765:8765 -v `pwd`/ydb_certs:/ydb_certs -e YDB_LOCAL_SURVIVE_RESTART=true -e YDB_USE_IN_MEMORY_PDISKS=true -h localhost cr.yandex/yc/yandex-docker-local-ydb:latest
+docker run -itd --name ydb -dp 2135:2135 -dp 2136:2136 -dp 8765:8765 -v `pwd`/ydb_certs:/ydb_certs -e YDB_LOCAL_SURVIVE_RESTART=true -e YDB_USE_IN_MEMORY_PDISKS=true -h localhost ydbplatform/local-ydb:latest
export YDB_CONNECTION_STRING="grpcs://localhost:2135/local"
export YDB_SSL_ROOT_CERTIFICATES_FILE="`pwd`/ydb_certs/ca.pem"
export YDB_SESSIONS_SHUTDOWN_URLS="http://localhost:8765/actors/kqp_proxy?force_shutdown=all"
diff --git a/README.md b/README.md
index eb51ff130..66967a039 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
[](https://ydb.tech)
[](https://github.com/ydb-platform/ydb-go-sdk/blob/master/CONTRIBUTING.md)
-Supports `table`, `discovery`, `coordination`, `ratelimiter`, `scheme`, `scripting` and `topic` clients for [YDB](https://ydb.tech).
+Supports `table`, `query`, `discovery`, `coordination`, `ratelimiter`, `scheme`, `scripting` and `topic` clients for [YDB](https://ydb.tech).
`YDB` is an open-source Distributed SQL Database that combines high availability and scalability with strict consistency and [ACID](https://en.wikipedia.org/wiki/ACID) transactions.
`YDB` was created primarily for [OLTP](https://en.wikipedia.org/wiki/Online_transaction_processing) workloads and supports some [OLAP](https://en.wikipedia.org/wiki/Online_analytical_processing) scenarious.
@@ -31,19 +31,20 @@ go get -u github.com/ydb-platform/ydb-go-sdk/v3
## Example Usage
* connect to YDB
-```golang
+```go
db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
if err != nil {
log.Fatal(err)
}
```
-* execute `SELECT` query
- ```golang
-const query = `SELECT 42 as id, "myStr" as myStr;`
-
+* execute `SELECT` query over `Table` service client
+ ```go
// Do retry operation on errors with best effort
-queryErr := db.Table().Do(ctx, func(ctx context.Context, s table.Session) (err error) {
- _, res, err := s.Execute(ctx, table.DefaultTxControl(), query, nil)
+err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) (err error) {
+ _, res, err := s.Execute(ctx, table.DefaultTxControl(),
+ `SELECT 42 as id, "myStr" as myStr;`,
+ nil, // empty parameters
+ )
if err != nil {
return err
}
@@ -62,12 +63,61 @@ queryErr := db.Table().Do(ctx, func(ctx context.Context, s table.Session) (err e
}
return res.Err() // for driver retry if not nil
})
-if queryErr != nil {
- log.Fatal(queryErr)
+if err != nil {
+ log.Fatal(err)
+}
+```
+* execute `SELECT` query over `Query` service client
+ ```go
+// Do retry operation on errors with best effort
+err := db.Query().Do( // Do retry operation on errors with best effort
+ ctx, // context manage exiting from Do
+ func(ctx context.Context, s query.Session) (err error) { // retry operation
+ _, res, err := s.Execute(ctx, `SELECT 42 as id, "myStr" as myStr;`))
+ if err != nil {
+ return err // for auto-retry with driver
+ }
+ defer func() { _ = res.Close(ctx) }() // cleanup resources
+ for { // iterate over result sets
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ for { // iterate over rows
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ type myStruct struct {
+ id uint64 `sql:"id"`
+ str string `sql:"myStr"`
+ }
+ var s myStruct
+ if err = row.ScanStruct(&s); err != nil {
+ return err // generally scan error not retryable, return it for driver check error
+ }
+ }
+ }
+
+ return res.Err() // return finally result error for auto-retry with driver
+ },
+ query.WithIdempotent(),
+)
+if err != nil {
+ log.Fatal(err)
}
```
+
* usage with `database/sql` (see additional docs in [SQL.md](SQL.md) )
-```golang
+```go
import (
"context"
"database/sql"
@@ -96,7 +146,7 @@ log.Printf("id = %d, myStr = \"%s\"", id, myStr)
```
-More examples of usage placed in [examples](https://github.com/ydb-platform/ydb-go-examples) repository.
+More examples of usage placed in [examples](./examples) directory.
## Credentials
@@ -124,8 +174,7 @@ Next packages provide debug tooling:
| [ydb-go-sdk-zap](https://github.com/ydb-platform/ydb-go-sdk-zap) | logging | logging ydb-go-sdk events with `zap` package | [ydbZap.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-zap/blob/master/internal/cmd/bench/main.go#L64) |
| [ydb-go-sdk-zerolog](https://github.com/ydb-platform/ydb-go-sdk-zerolog) | logging | logging ydb-go-sdk events with `zerolog` package | [ydbZerolog.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-zerolog/blob/master/internal/cmd/bench/main.go#L47) |
| [ydb-go-sdk-logrus](https://github.com/ydb-platform/ydb-go-sdk-logrus) | logging | logging ydb-go-sdk events with `logrus` package | [ydbLogrus.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-logrus/blob/master/internal/cmd/bench/main.go#L48) |
-| [ydb-go-sdk-metrics](https://github.com/ydb-platform/ydb-go-sdk-metrics) | metrics | common metrics of ydb-go-sdk. Package declare interfaces such as `Registry`, `GaugeVec` and `Gauge` and use it for traces | |
-| [ydb-go-sdk-prometheus](https://github.com/ydb-platform/ydb-go-sdk-prometheus) | metrics | prometheus wrapper over [ydb-go-sdk-metrics](https://github.com/ydb-platform/ydb-go-sdk-metrics) | [ydbPrometheus.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-prometheus/blob/master/internal/cmd/bench/main.go#L56) |
+| [ydb-go-sdk-prometheus](https://github.com/ydb-platform/ydb-go-sdk-prometheus/v2) | metrics | prometheus wrapper over [ydb-go-sdk/v3/metrics](https://github.com/ydb-platform/ydb-go-sdk/tree/master/metrics) | [ydbPrometheus.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-prometheus/blob/master/internal/cmd/bench/main.go#L56) |
| [ydb-go-sdk-opentracing](https://github.com/ydb-platform/ydb-go-sdk-opentracing) | tracing | OpenTracing plugin for trace internal ydb-go-sdk calls | [ydbOpentracing.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-opentracing/blob/master/internal/cmd/bench/main.go#L86) |
| [ydb-go-sdk-otel](https://github.com/ydb-platform/ydb-go-sdk-otel) | tracing | OpenTelemetry plugin for trace internal ydb-go-sdk calls | [ydbOtel.WithTraces](https://github.com/ydb-platform/ydb-go-sdk-otel/blob/master/internal/cmd/bench/main.go#L98) |
diff --git a/balancers/config.go b/balancers/config.go
index ee70ec57f..8bc38199c 100644
--- a/balancers/config.go
+++ b/balancers/config.go
@@ -116,9 +116,9 @@ func FromConfig(config string, opts ...fromConfigOption) *balancerConfig.Config
b *balancerConfig.Config
err error
)
- for _, o := range opts {
- if o != nil {
- o(&h)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&h)
}
}
diff --git a/config/config.go b/config/config.go
index e3578ee58..96756e9ec 100644
--- a/config/config.go
+++ b/config/config.go
@@ -161,9 +161,19 @@ func WithTraceRetry(t *trace.Retry, opts ...trace.RetryComposeOption) Option {
}
}
+// WithApplicationName add provided application name to all api requests
+func WithApplicationName(applicationName string) Option {
+ return func(c *Config) {
+ c.metaOptions = append(c.metaOptions, meta.WithApplicationNameOption(applicationName))
+ }
+}
+
+// WithUserAgent add provided user agent to all api requests
+//
+// Deprecated: use WithApplicationName instead
func WithUserAgent(userAgent string) Option {
return func(c *Config) {
- c.metaOptions = append(c.metaOptions, meta.WithUserAgentOption(userAgent))
+ c.metaOptions = append(c.metaOptions, meta.WithApplicationNameOption(userAgent))
}
}
@@ -268,9 +278,9 @@ func ExcludeGRPCCodesForPessimization(codes ...grpcCodes.Code) Option {
func New(opts ...Option) *Config {
c := defaultConfig()
- for _, o := range opts {
- if o != nil {
- o(c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
}
}
@@ -281,9 +291,9 @@ func New(opts ...Option) *Config {
// With makes copy of current Config with specified options
func (c *Config) With(opts ...Option) *Config {
- for _, o := range opts {
- if o != nil {
- o(c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
}
}
c.meta = meta.New(
diff --git a/connection.go b/connection.go
index 3e8e3d2a4..9bff94e31 100644
--- a/connection.go
+++ b/connection.go
@@ -5,6 +5,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/coordination"
"github.com/ydb-platform/ydb-go-sdk/v3/discovery"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
"github.com/ydb-platform/ydb-go-sdk/v3/ratelimiter"
"github.com/ydb-platform/ydb-go-sdk/v3/scheme"
"github.com/ydb-platform/ydb-go-sdk/v3/scripting"
@@ -34,6 +35,13 @@ type Connection interface {
// Table returns table client
Table() table.Client
+ // Query returns query client
+ //
+ // # Experimental
+ //
+ // Notice: This API is EXPERIMENTAL and may be changed or removed in a later release.
+ Query() query.Client
+
// Scheme returns scheme client
Scheme() scheme.Client
diff --git a/coordination/coordination.go b/coordination/coordination.go
index 7bd9f6dbf..bfe6e9870 100644
--- a/coordination/coordination.go
+++ b/coordination/coordination.go
@@ -2,7 +2,11 @@ package coordination
import (
"context"
+ "fmt"
+ "math"
+ "time"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
"github.com/ydb-platform/ydb-go-sdk/v3/scheme"
)
@@ -11,4 +15,187 @@ type Client interface {
AlterNode(ctx context.Context, path string, config NodeConfig) (err error)
DropNode(ctx context.Context, path string) (err error)
DescribeNode(ctx context.Context, path string) (_ *scheme.Entry, _ *NodeConfig, err error)
+
+ // Session starts a new session. This method blocks until the server session is created. The context provided
+ // may be used to cancel the invocation. If the method completes successfully, the session remains alive even if
+ // the context is canceled.
+ //
+ // To ensure resources are not leaked, one of the following actions must be performed:
+ //
+ // - call Close on the Session,
+ // - close the Client which the session was created with,
+ // - call any method of the Session until the ErrSessionClosed is returned.
+ //
+ // # Experimental
+ //
+ // Notice: This API is EXPERIMENTAL and may be changed or removed in a later release.
+ Session(ctx context.Context, path string, opts ...options.SessionOption) (Session, error)
+}
+
+const (
+ // MaxSemaphoreLimit defines the maximum value of the limit parameter in the Session.CreateSemaphore method.
+ MaxSemaphoreLimit = math.MaxUint64
+
+ // Exclusive is just a shortcut for the maximum semaphore limit value. You can use this to acquire a semaphore in
+ // the exclusive mode if it was created with the limit value of MaxSemaphoreLimit, which is always true for
+ // ephemeral semaphores.
+ Exclusive = math.MaxUint64
+
+ // Shared is just a shortcut for the minimum semaphore limit value (1). You can use this to acquire a semaphore in
+ // the shared mode if it was created with the limit value of MaxSemaphoreLimit, which is always true for ephemeral
+ // semaphores.
+ Shared = 1
+)
+
+// Session defines a coordination service backed session.
+//
+// In general, Session API is concurrency-friendly, you can safely access all of its methods concurrently.
+//
+// The client guarantees that sequential calls of the methods are sent to the server in the same order. However, the
+// session client may reorder and suspend some of the requests without violating correctness of the execution. This also
+// applies to the situations when the underlying gRPC stream has been recreated due to network or server issues.
+//
+// The client automatically keeps the underlying gRPC stream alive by sending keep-alive (ping-pong) requests. If the
+// client can no longer consider the session alive, it immediately cancels the session context which also leads to
+// cancellation of contexts of all semaphore leases created by this session.
+type Session interface {
+ // Close closes the coordination service session. It cancels all active requests on the server and notifies every
+ // pending or waiting for response request on the client side. It also cancels the session context and tries to
+ // stop the session gracefully on the server. If the ctx is canceled, this will not wait for the server session to
+ // become stopped and returns immediately with an error. Once this function returns with no error, all subsequent
+ // calls will be noop.
+ Close(ctx context.Context) error
+
+ // Context returns the context of the session. It is canceled when the underlying server session is over or if the
+ // client could not get any successful response from the server before the session timeout (see
+ // options.WithSessionTimeout).
+ Context() context.Context
+
+ // CreateSemaphore creates a new semaphore. This method waits until the server successfully creates a new semaphore
+ // or returns an error.
+ //
+ // This method is not idempotent. If the request has been sent to the server but no reply has been received, it
+ // returns the ErrOperationStatusUnknown error.
+ CreateSemaphore(ctx context.Context, name string, limit uint64, opts ...options.CreateSemaphoreOption) error
+
+ // UpdateSemaphore changes semaphore data. This method waits until the server successfully updates the semaphore or
+ // returns an error.
+ //
+ // This method is not idempotent. The client will automatically retry in the case of network or server failure
+ // unless it leaves the client state inconsistent.
+ UpdateSemaphore(ctx context.Context, name string, opts ...options.UpdateSemaphoreOption) error
+
+ // DeleteSemaphore deletes an existing semaphore. This method waits until the server successfully deletes the
+ // semaphore or returns an error.
+ //
+ // This method is not idempotent. If the request has been sent to the server but no reply has been received, it
+ // returns the ErrOperationStatusUnknown error.
+ DeleteSemaphore(ctx context.Context, name string, opts ...options.DeleteSemaphoreOption) error
+
+ // DescribeSemaphore returns the state of the semaphore.
+ //
+ // This method is idempotent. The client will automatically retry in the case of network or server failure.
+ DescribeSemaphore(
+ ctx context.Context,
+ name string,
+ opts ...options.DescribeSemaphoreOption,
+ ) (*SemaphoreDescription, error)
+
+ // AcquireSemaphore acquires the semaphore. If you acquire an ephemeral semaphore (see options.WithEphemeral), its
+ // limit will be set to MaxSemaphoreLimit. Later requests override previous operations with the same semaphore, e.g.
+ // to reduce acquired count, change timeout or attached data.
+ //
+ // This method blocks until the semaphore is acquired, an error is returned from the server or the session is
+ // closed. If the operation context was canceled but the server replied that the semaphore was actually acquired,
+ // the client will automatically release the semaphore.
+ //
+ // Semaphore waiting is fair: the semaphore guarantees that other sessions invoking the AcquireSemaphore method
+ // acquire permits in the order which they were called (FIFO). If a session invokes the AcquireSemaphore method
+ // multiple times while the first invocation is still in process, the position in the queue remains unchanged.
+ //
+ // This method is idempotent. The client will automatically retry in the case of network or server failure.
+ AcquireSemaphore(
+ ctx context.Context,
+ name string,
+ count uint64,
+ opts ...options.AcquireSemaphoreOption,
+ ) (Lease, error)
+
+ // SessionID returns a server-generated identifier of the session. This value is permanent and unique within the
+ // coordination service node.
+ SessionID() uint64
+
+ // Reconnect forcibly shuts down the underlying gRPC stream and initiates a new one. This method is highly unlikely
+ // to be of use in a typical application but is extremely useful for testing an API implementation.
+ Reconnect()
+}
+
+// Lease is the object which defines the rights of the session to the acquired semaphore. Lease is alive until its
+// context is not canceled. This may happen implicitly, when the associated session becomes lost or closed, or
+// explicitly, if someone calls the Release method of the lease.
+type Lease interface {
+ // Context returns the context of the lease. It is canceled when the session it was created by was lost or closed,
+ // or if the lease was released by calling the Release method.
+ Context() context.Context
+
+ // Release releases the acquired lease to the semaphore. It also cancels the context of the lease. This method does
+ // not take a ctx argument, but you can cancel the execution of it by closing the session or canceling its context.
+ Release() error
+
+ // Session returns the session which this lease was created by.
+ Session() Session
+}
+
+// SemaphoreDescription describes the state of a semaphore.
+type SemaphoreDescription struct {
+ // Name is the name of the semaphore.
+ Name string
+
+ // Limit is the maximum number of tokens that may be acquired.
+ Limit uint64
+
+ // Count is the number of tokens currently acquired by its owners.
+ Count uint64
+
+ // Ephemeral semaphores are deleted when there are no owners and waiters left.
+ Ephemeral bool
+
+ // Data is user-defined data attached to the semaphore.
+ Data []byte
+
+ // Owner is the list of current owners of the semaphore.
+ Owners []*SemaphoreSession
+
+ // Waiter is the list of current waiters of the semaphore.
+ Waiters []*SemaphoreSession
+}
+
+// SemaphoreSession describes an owner or a waiter of this semaphore.
+type SemaphoreSession struct {
+ // SessionID is the id of the session which tried to acquire the semaphore.
+ SessionID uint64
+
+ // Count is the number of tokens for the acquire operation.
+ Count uint64
+
+ // OrderId is a monotonically increasing id which determines locking order.
+ OrderID uint64
+
+ // Data is user-defined data attached to the acquire operation.
+ Data []byte
+
+ // Timeout is the timeout for the operation in the waiter queue. If this is time.Duration(math.MaxInt64) the session
+ // will wait for the semaphore until the operation is canceled.
+ Timeout time.Duration
+}
+
+func (d *SemaphoreDescription) String() string {
+ return fmt.Sprintf(
+ "{Name: %q Limit: %d Count: %d Ephemeral: %t Data: %q Owners: %s Waiters: %s}",
+ d.Name, d.Limit, d.Count, d.Ephemeral, d.Data, d.Owners, d.Waiters)
+}
+
+func (s *SemaphoreSession) String() string {
+ return fmt.Sprintf("{SessionID: %d Count: %d OrderID: %d Data: %q TimeoutMillis: %v}",
+ s.SessionID, s.Count, s.OrderID, s.Data, s.Timeout)
}
diff --git a/coordination/errors.go b/coordination/errors.go
new file mode 100644
index 000000000..2f10cd557
--- /dev/null
+++ b/coordination/errors.go
@@ -0,0 +1,18 @@
+package coordination
+
+import "errors"
+
+var (
+ // ErrOperationStatusUnknown indicates that the request has been sent to the server but no reply has been received.
+ // The client usually automatically retries calls of that kind, but there are cases when it is not possible:
+ // - the request is not idempotent, non-idempotent requests are never retried,
+ // - the session was lost and its context is canceled.
+ ErrOperationStatusUnknown = errors.New("operation status is unknown")
+
+ // ErrSessionClosed indicates that the Session object is closed.
+ ErrSessionClosed = errors.New("session is closed")
+
+ // ErrAcquireTimeout indicates that the Session.AcquireSemaphore method could not acquire the semaphore before the
+ // operation timeout (see options.WithAcquireTimeout).
+ ErrAcquireTimeout = errors.New("acquire semaphore timeout")
+)
diff --git a/coordination/example_test.go b/coordination/example_test.go
index 57657d779..20ca57852 100644
--- a/coordination/example_test.go
+++ b/coordination/example_test.go
@@ -6,10 +6,11 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3"
"github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
)
//nolint:errcheck
-func Example() {
+func Example_createDropNode() {
ctx := context.TODO()
db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
if err != nil {
@@ -41,3 +42,106 @@ func Example() {
}
fmt.Printf("node description: %+v\nnode config: %+v\n", e, c)
}
+
+func Example_semaphore() {
+ ctx := context.TODO()
+ db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
+ if err != nil {
+ fmt.Printf("failed to connect: %v", err)
+
+ return
+ }
+ defer db.Close(ctx) // cleanup resources
+ // create node
+ err = db.Coordination().CreateNode(ctx, "/local/test", coordination.NodeConfig{
+ Path: "",
+ SelfCheckPeriodMillis: 1000,
+ SessionGracePeriodMillis: 1000,
+ ReadConsistencyMode: coordination.ConsistencyModeStrict,
+ AttachConsistencyMode: coordination.ConsistencyModeStrict,
+ RatelimiterCountersMode: coordination.RatelimiterCountersModeDetailed,
+ })
+ if err != nil {
+ fmt.Printf("failed to create node: %v", err)
+
+ return
+ }
+ defer func() {
+ dropNodeErr := db.Coordination().DropNode(ctx, "/local/test")
+ if dropNodeErr != nil {
+ fmt.Printf("failed to drop node: %v\n", dropNodeErr)
+ }
+ }()
+
+ e, c, err := db.Coordination().DescribeNode(ctx, "/local/test")
+ if err != nil {
+ fmt.Printf("failed to describe node: %v\n", err)
+
+ return
+ }
+ fmt.Printf("node description: %+v\nnode config: %+v\n", e, c)
+
+ s, err := db.Coordination().Session(ctx, "/local/test")
+ if err != nil {
+ fmt.Printf("failed to create session: %v\n", err)
+
+ return
+ }
+ defer s.Close(ctx)
+ fmt.Printf("session 1 created, id: %d\n", s.SessionID())
+
+ err = s.CreateSemaphore(ctx, "my-semaphore", 20, options.WithCreateData([]byte{1, 2, 3}))
+ if err != nil {
+ fmt.Printf("failed to create semaphore: %v", err)
+
+ return
+ }
+ fmt.Printf("semaphore my-semaphore created\n")
+
+ lease, err := s.AcquireSemaphore(ctx, "my-semaphore", 10)
+ if err != nil {
+ fmt.Printf("failed to acquire semaphore: %v", err)
+
+ return
+ }
+ defer func() {
+ releaseErr := lease.Release()
+ if releaseErr != nil {
+ fmt.Printf("failed to release lease: %v", releaseErr)
+ }
+ }()
+
+ fmt.Printf("session 1 acquired semaphore 10\n")
+
+ s.Reconnect()
+ fmt.Printf("session 1 reconnected\n")
+
+ desc, err := s.DescribeSemaphore(
+ ctx,
+ "my-semaphore",
+ options.WithDescribeOwners(true),
+ options.WithDescribeWaiters(true),
+ )
+ if err != nil {
+ fmt.Printf("failed to describe semaphore: %v", err)
+
+ return
+ }
+ fmt.Printf("session 1 described semaphore %v\n", desc)
+
+ err = lease.Release()
+ if err != nil {
+ fmt.Printf("failed to release semaphore: %v", err)
+
+ return
+ }
+ fmt.Printf("session 1 released semaphore my-semaphore\n")
+
+ err = s.DeleteSemaphore(ctx, "my-semaphore", options.WithForceDelete(true))
+ if err != nil {
+ fmt.Printf("failed to delete semaphore: %v", err)
+
+ return
+ }
+ fmt.Printf("deleted semaphore my-semaphore\n")
+}
diff --git a/coordination/options/options.go b/coordination/options/options.go
new file mode 100644
index 000000000..e156bc068
--- /dev/null
+++ b/coordination/options/options.go
@@ -0,0 +1,173 @@
+package options
+
+import (
+ "math"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+)
+
+// WithDescription returns an SessionOption that specifies a user-defined description that may be used to describe
+// the client.
+func WithDescription(description string) SessionOption {
+ return func(c *CreateSessionOptions) {
+ c.Description = description
+ }
+}
+
+// WithSessionTimeout returns an SessionOption that specifies the timeout during which client may restore a
+// detached session. The client is forced to terminate the session if the last successful session request occurred
+// earlier than this time.
+//
+// If this is not set, the client uses the default 5 seconds.
+func WithSessionTimeout(timeout time.Duration) SessionOption {
+ return func(c *CreateSessionOptions) {
+ c.SessionTimeout = timeout
+ }
+}
+
+// WithSessionStartTimeout returns an SessionOption that specifies the time that the client should wait for a
+// response to the StartSession request from the server before it terminates the gRPC stream and tries to reconnect.
+//
+// If this is not set, the client uses the default time 1 second.
+func WithSessionStartTimeout(timeout time.Duration) SessionOption {
+ return func(c *CreateSessionOptions) {
+ c.SessionStartTimeout = timeout
+ }
+}
+
+// WithSessionStopTimeout returns an SessionOption that specifies the time that the client should wait for a
+// response to the StopSession request from the server before it terminates the gRPC stream and tries to reconnect.
+//
+// If this is not set, the client uses the default time 1 second.
+func WithSessionStopTimeout(timeout time.Duration) SessionOption {
+ return func(c *CreateSessionOptions) {
+ c.SessionStartTimeout = timeout
+ }
+}
+
+// WithSessionKeepAliveTimeout returns an SessionOption that specifies the time that the client will wait before
+// it terminates the gRPC stream and tries to reconnect if no successful responses have been received from the server.
+//
+// If this is not set, the client uses the default time 10 seconds.
+func WithSessionKeepAliveTimeout(timeout time.Duration) SessionOption {
+ return func(c *CreateSessionOptions) {
+ c.SessionKeepAliveTimeout = timeout
+ }
+}
+
+// WithSessionReconnectDelay returns an SessionOption that specifies the time that the client will wait before it
+// tries to reconnect the underlying gRPC stream in case of error.
+//
+// If this is not set, the client uses the default time 500 milliseconds.
+func WithSessionReconnectDelay(delay time.Duration) SessionOption {
+ return func(c *CreateSessionOptions) {
+ c.SessionReconnectDelay = delay
+ }
+}
+
+// SessionOption configures how we create a new session.
+type SessionOption func(c *CreateSessionOptions)
+
+// CreateSessionOptions configure an Session call. CreateSessionOptions are set by the SessionOption values
+// passed to the Session function.
+type CreateSessionOptions struct {
+ Description string
+ SessionTimeout time.Duration
+ SessionStartTimeout time.Duration
+ SessionStopTimeout time.Duration
+ SessionKeepAliveTimeout time.Duration
+ SessionReconnectDelay time.Duration
+}
+
+// WithEphemeral returns an AcquireSemaphoreOption that causes to create an ephemeral semaphore.
+//
+// Ephemeral semaphores are created with the first acquire operation and automatically deleted with the last release
+// operation. Ephemeral semaphore are always created with the limit of coordination.MaxSemaphoreLimit.
+func WithEphemeral(ephemeral bool) AcquireSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_AcquireSemaphore) {
+ c.Ephemeral = ephemeral
+ }
+}
+
+// WithAcquireTimeout returns an AcquireSemaphoreOption which sets the timeout after which the operation fails if it
+// is still waiting in the queue. Use 0 to make the AcquireSemaphore method fail immediately if the semaphore is already
+// acquired by another session.
+//
+// If this is not set, the client waits for the acquire operation result until the operation or session context is done.
+// You can reset the default value of this timeout by calling the WithAcquireInfiniteTimeout method.
+func WithAcquireTimeout(timeout time.Duration) AcquireSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_AcquireSemaphore) {
+ c.TimeoutMillis = uint64(timeout.Milliseconds())
+ }
+}
+
+// WithAcquireInfiniteTimeout returns an AcquireSemaphoreOption which disables the timeout after which the operation
+// fails if it is still waiting in the queue.
+//
+// This is the default behavior. You can set the specific timeout by calling the WithAcquireTimeout method.
+func WithAcquireInfiniteTimeout() AcquireSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_AcquireSemaphore) {
+ c.TimeoutMillis = math.MaxUint64
+ }
+}
+
+// WithAcquireData returns an AcquireSemaphoreOption which attaches user-defined data to the operation.
+func WithAcquireData(data []byte) AcquireSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_AcquireSemaphore) {
+ c.Data = data
+ }
+}
+
+// AcquireSemaphoreOption configures how we acquire a semaphore.
+type AcquireSemaphoreOption func(c *Ydb_Coordination.SessionRequest_AcquireSemaphore)
+
+// WithForceDelete return a DeleteSemaphoreOption which allows to delete a semaphore even if it is currently acquired
+// by other sessions.
+func WithForceDelete(force bool) DeleteSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_DeleteSemaphore) {
+ c.Force = force
+ }
+}
+
+// DeleteSemaphoreOption configures how we delete a semaphore.
+type DeleteSemaphoreOption func(c *Ydb_Coordination.SessionRequest_DeleteSemaphore)
+
+// WithCreateData return a CreateSemaphoreOption which attaches user-defined data to the semaphore.
+func WithCreateData(data []byte) CreateSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_CreateSemaphore) {
+ c.Data = data
+ }
+}
+
+// CreateSemaphoreOption configures how we create a semaphore.
+type CreateSemaphoreOption func(c *Ydb_Coordination.SessionRequest_CreateSemaphore)
+
+// WithUpdateData return a UpdateSemaphoreOption which changes user-defined data in the semaphore.
+func WithUpdateData(data []byte) UpdateSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_UpdateSemaphore) {
+ c.Data = data
+ }
+}
+
+// UpdateSemaphoreOption configures how we update a semaphore.
+type UpdateSemaphoreOption func(c *Ydb_Coordination.SessionRequest_UpdateSemaphore)
+
+// WithDescribeOwners return a DescribeSemaphoreOption which causes server send the list of owners in the response
+// to the DescribeSemaphore request.
+func WithDescribeOwners(describeOwners bool) DescribeSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_DescribeSemaphore) {
+ c.IncludeOwners = describeOwners
+ }
+}
+
+// WithDescribeWaiters return a DescribeSemaphoreOption which causes server send the list of waiters in the response
+// to the DescribeSemaphore request.
+func WithDescribeWaiters(describeWaiters bool) DescribeSemaphoreOption {
+ return func(c *Ydb_Coordination.SessionRequest_DescribeSemaphore) {
+ c.IncludeWaiters = describeWaiters
+ }
+}
+
+// DescribeSemaphoreOption configures how we update a semaphore.
+type DescribeSemaphoreOption func(c *Ydb_Coordination.SessionRequest_DescribeSemaphore)
diff --git a/driver.go b/driver.go
index 65eb86a81..7adfab361 100644
--- a/driver.go
+++ b/driver.go
@@ -20,6 +20,8 @@ import (
discoveryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/discovery/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/dsn"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/endpoint"
+ internalQuery "github.com/ydb-platform/ydb-go-sdk/v3/internal/query"
+ queryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/config"
internalRatelimiter "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter"
ratelimiterConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/config"
internalScheme "github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme"
@@ -35,6 +37,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
"github.com/ydb-platform/ydb-go-sdk/v3/log"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
"github.com/ydb-platform/ydb-go-sdk/v3/ratelimiter"
"github.com/ydb-platform/ydb-go-sdk/v3/scheme"
"github.com/ydb-platform/ydb-go-sdk/v3/scripting"
@@ -47,7 +50,7 @@ import (
var _ Connection = (*Driver)(nil)
// Driver type provide access to YDB service clients
-type Driver struct { //nolint:maligned
+type Driver struct {
ctx context.Context // cancel while Driver.Close called.
ctxCancel context.CancelFunc
@@ -62,25 +65,28 @@ type Driver struct { //nolint:maligned
config *config.Config
options []config.Option
- discovery *internalDiscovery.Client
+ discovery *xsync.Once[*internalDiscovery.Client]
discoveryOptions []discoveryConfig.Option
- table *internalTable.Client
+ table *xsync.Once[*internalTable.Client]
tableOptions []tableConfig.Option
- scripting *internalScripting.Client
+ query *xsync.Once[*internalQuery.Client]
+ queryOptions []queryConfig.Option
+
+ scripting *xsync.Once[*internalScripting.Client]
scriptingOptions []scriptingConfig.Option
- scheme *internalScheme.Client
+ scheme *xsync.Once[*internalScheme.Client]
schemeOptions []schemeConfig.Option
- coordination *internalCoordination.Client
+ coordination *xsync.Once[*internalCoordination.Client]
coordinationOptions []coordinationConfig.Option
- ratelimiter *internalRatelimiter.Client
+ ratelimiter *xsync.Once[*internalRatelimiter.Client]
ratelimiterOptions []ratelimiterConfig.Option
- topic *topicclientinternal.Client
+ topic *xsync.Once[*topicclientinternal.Client]
topicOptions []topicoptions.TopicOption
databaseSQLOptions []xsql.ConnectorOption
@@ -109,7 +115,9 @@ func (d *Driver) trace() *trace.Driver {
//
//nolint:nonamedreturns
func (d *Driver) Close(ctx context.Context) (finalErr error) {
- onDone := trace.DriverOnClose(d.trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.DriverOnClose(d.trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/ydb.(*Driver).Close"),
+ )
defer func() {
onDone(finalErr)
}()
@@ -141,6 +149,7 @@ func (d *Driver) Close(ctx context.Context) (finalErr error) {
d.scheme.Close,
d.scripting.Close,
d.table.Close,
+ d.query.Close,
d.topic.Close,
d.balancer.Close,
d.pool.Release,
@@ -177,37 +186,46 @@ func (d *Driver) Secure() bool {
// Table returns table client
func (d *Driver) Table() table.Client {
- return d.table
+ return d.table.Get()
+}
+
+// Query returns query client
+//
+// # Experimental
+//
+// Notice: This API is EXPERIMENTAL and may be changed or removed in a later release.
+func (d *Driver) Query() query.Client {
+ return d.query.Get()
}
// Scheme returns scheme client
func (d *Driver) Scheme() scheme.Client {
- return d.scheme
+ return d.scheme.Get()
}
// Coordination returns coordination client
func (d *Driver) Coordination() coordination.Client {
- return d.coordination
+ return d.coordination.Get()
}
// Ratelimiter returns ratelimiter client
func (d *Driver) Ratelimiter() ratelimiter.Client {
- return d.ratelimiter
+ return d.ratelimiter.Get()
}
// Discovery returns discovery client
func (d *Driver) Discovery() discovery.Client {
- return d.discovery
+ return d.discovery.Get()
}
// Scripting returns scripting client
func (d *Driver) Scripting() scripting.Client {
- return d.scripting
+ return d.scripting.Get()
}
// Topic returns topic client
func (d *Driver) Topic() topic.Client {
- return d.topic
+ return d.topic.Get()
}
// Open connects to database by DSN and return driver runtime holder
@@ -232,7 +250,7 @@ func Open(ctx context.Context, dsn string, opts ...Option) (_ *Driver, err error
onDone := trace.DriverOnInit(
d.trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/ydb.Open"),
d.config.Endpoint(), d.config.Database(), d.config.Secure(),
)
defer func() {
@@ -268,7 +286,7 @@ func New(ctx context.Context, opts ...Option) (_ *Driver, err error) {
onDone := trace.DriverOnInit(
d.trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/ydb.New"),
d.config.Endpoint(), d.config.Database(), d.config.Secure(),
)
defer func() {
@@ -284,7 +302,7 @@ func New(ctx context.Context, opts ...Option) (_ *Driver, err error) {
//nolint:cyclop, nonamedreturns
func newConnectionFromOptions(ctx context.Context, opts ...Option) (_ *Driver, err error) {
- ctx, driverCtxCancel := xcontext.WithCancel(xcontext.WithoutDeadline(ctx))
+ ctx, driverCtxCancel := xcontext.WithCancel(xcontext.ValueOnly(ctx))
defer func() {
if err != nil {
driverCtxCancel()
@@ -332,6 +350,7 @@ func newConnectionFromOptions(ctx context.Context, opts ...Option) (_ *Driver, e
for _, opt := range []Option{
WithTraceDriver(log.Driver(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck
WithTraceTable(log.Table(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck
+ WithTraceQuery(log.Query(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck
WithTraceScripting(log.Scripting(d.logger, d.loggerDetails, d.loggerOpts...)), //nolint:contextcheck
WithTraceScheme(log.Scheme(d.logger, d.loggerDetails, d.loggerOpts...)),
WithTraceCoordination(log.Coordination(d.logger, d.loggerDetails, d.loggerOpts...)),
@@ -383,122 +402,133 @@ func (d *Driver) connect(ctx context.Context) (err error) {
return xerrors.WithStackTrace(err)
}
- d.table, err = internalTable.New(ctx,
- d.balancer,
- tableConfig.New(
- append(
- // prepend common params from root config
- []tableConfig.Option{
- tableConfig.With(d.config.Common),
- },
- d.tableOptions...,
- )...,
- ),
- )
- if err != nil {
- return xerrors.WithStackTrace(err)
- }
+ d.table = xsync.OnceValue(func() *internalTable.Client {
+ return internalTable.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ tableConfig.New(
+ append(
+ // prepend common params from root config
+ []tableConfig.Option{
+ tableConfig.With(d.config.Common),
+ },
+ d.tableOptions...,
+ )...,
+ ),
+ )
+ })
- d.scheme, err = internalScheme.New(ctx,
- d.balancer,
- schemeConfig.New(
- append(
- // prepend common params from root config
- []schemeConfig.Option{
- schemeConfig.WithDatabaseName(d.Name()),
- schemeConfig.With(d.config.Common),
- },
- d.schemeOptions...,
- )...,
- ),
- )
+ d.query = xsync.OnceValue(func() *internalQuery.Client {
+ return internalQuery.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ queryConfig.New(
+ append(
+ // prepend common params from root config
+ []queryConfig.Option{
+ queryConfig.With(d.config.Common),
+ },
+ d.queryOptions...,
+ )...,
+ ),
+ )
+ })
if err != nil {
return xerrors.WithStackTrace(err)
}
- d.coordination, err = internalCoordination.New(ctx,
- d.balancer,
- coordinationConfig.New(
- append(
- // prepend common params from root config
- []coordinationConfig.Option{
- coordinationConfig.With(d.config.Common),
- },
- d.coordinationOptions...,
- )...,
- ),
- )
- if err != nil {
- return xerrors.WithStackTrace(err)
- }
+ d.scheme = xsync.OnceValue(func() *internalScheme.Client {
+ return internalScheme.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ schemeConfig.New(
+ append(
+ // prepend common params from root config
+ []schemeConfig.Option{
+ schemeConfig.WithDatabaseName(d.Name()),
+ schemeConfig.With(d.config.Common),
+ },
+ d.schemeOptions...,
+ )...,
+ ),
+ )
+ })
- d.ratelimiter, err = internalRatelimiter.New(ctx,
- d.balancer,
- ratelimiterConfig.New(
- append(
- // prepend common params from root config
- []ratelimiterConfig.Option{
- ratelimiterConfig.With(d.config.Common),
- },
- d.ratelimiterOptions...,
- )...,
- ),
- )
- if err != nil {
- return xerrors.WithStackTrace(err)
- }
+ d.coordination = xsync.OnceValue(func() *internalCoordination.Client {
+ return internalCoordination.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ coordinationConfig.New(
+ append(
+ // prepend common params from root config
+ []coordinationConfig.Option{
+ coordinationConfig.With(d.config.Common),
+ },
+ d.coordinationOptions...,
+ )...,
+ ),
+ )
+ })
- d.discovery, err = internalDiscovery.New(ctx,
- d.pool.Get(endpoint.New(d.config.Endpoint())),
- discoveryConfig.New(
- append(
- // prepend common params from root config
- []discoveryConfig.Option{
- discoveryConfig.With(d.config.Common),
- discoveryConfig.WithEndpoint(d.Endpoint()),
- discoveryConfig.WithDatabase(d.Name()),
- discoveryConfig.WithSecure(d.Secure()),
- discoveryConfig.WithMeta(d.config.Meta()),
- },
- d.discoveryOptions...,
- )...,
- ),
- )
- if err != nil {
- return xerrors.WithStackTrace(err)
- }
+ d.ratelimiter = xsync.OnceValue(func() *internalRatelimiter.Client {
+ return internalRatelimiter.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ ratelimiterConfig.New(
+ append(
+ // prepend common params from root config
+ []ratelimiterConfig.Option{
+ ratelimiterConfig.With(d.config.Common),
+ },
+ d.ratelimiterOptions...,
+ )...,
+ ),
+ )
+ })
- d.scripting, err = internalScripting.New(ctx,
- d.balancer,
- scriptingConfig.New(
+ d.discovery = xsync.OnceValue(func() *internalDiscovery.Client {
+ return internalDiscovery.New(xcontext.ValueOnly(ctx),
+ d.pool.Get(endpoint.New(d.config.Endpoint())),
+ discoveryConfig.New(
+ append(
+ // prepend common params from root config
+ []discoveryConfig.Option{
+ discoveryConfig.With(d.config.Common),
+ discoveryConfig.WithEndpoint(d.Endpoint()),
+ discoveryConfig.WithDatabase(d.Name()),
+ discoveryConfig.WithSecure(d.Secure()),
+ discoveryConfig.WithMeta(d.config.Meta()),
+ },
+ d.discoveryOptions...,
+ )...,
+ ),
+ )
+ })
+
+ d.scripting = xsync.OnceValue(func() *internalScripting.Client {
+ return internalScripting.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ scriptingConfig.New(
+ append(
+ // prepend common params from root config
+ []scriptingConfig.Option{
+ scriptingConfig.With(d.config.Common),
+ },
+ d.scriptingOptions...,
+ )...,
+ ),
+ )
+ })
+
+ d.topic = xsync.OnceValue(func() *topicclientinternal.Client {
+ return topicclientinternal.New(xcontext.ValueOnly(ctx),
+ d.balancer,
+ d.config.Credentials(),
append(
// prepend common params from root config
- []scriptingConfig.Option{
- scriptingConfig.With(d.config.Common),
+ []topicoptions.TopicOption{
+ topicoptions.WithOperationTimeout(d.config.OperationTimeout()),
+ topicoptions.WithOperationCancelAfter(d.config.OperationCancelAfter()),
},
- d.scriptingOptions...,
+ d.topicOptions...,
)...,
- ),
- )
- if err != nil {
- return xerrors.WithStackTrace(err)
- }
-
- d.topic, err = topicclientinternal.New(ctx,
- d.balancer,
- d.config.Credentials(),
- append(
- // prepend common params from root config
- []topicoptions.TopicOption{
- topicoptions.WithOperationTimeout(d.config.OperationTimeout()),
- topicoptions.WithOperationCancelAfter(d.config.OperationCancelAfter()),
- },
- d.topicOptions...,
- )...,
- )
- if err != nil {
- return xerrors.WithStackTrace(err)
- }
+ )
+ })
return nil
}
diff --git a/example_test.go b/example_test.go
index 3b06248e6..399411e68 100644
--- a/example_test.go
+++ b/example_test.go
@@ -3,6 +3,7 @@ package ydb_test
import (
"context"
"database/sql"
+ "errors"
"fmt"
"io"
"log"
@@ -14,6 +15,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3"
"github.com/ydb-platform/ydb-go-sdk/v3/balancers"
"github.com/ydb-platform/ydb-go-sdk/v3/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/named"
@@ -21,6 +23,69 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions"
)
+//nolint:testableexamples, nonamedreturns
+func Example_query() {
+ ctx := context.TODO()
+ db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer db.Close(ctx) // cleanup resources
+
+ err = db.Query().Do( // Do retry operation on errors with best effort
+ ctx, // context manage exiting from Do
+ func(ctx context.Context, s query.Session) (err error) { // retry operation
+ _, res, err := s.Execute(ctx,
+ `SELECT $id as myId, $str as myStr`,
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$id").Uint64(42).
+ Param("$str").Text("my string").
+ Build(),
+ ),
+ )
+ if err != nil {
+ return err // for auto-retry with driver
+ }
+ defer func() { _ = res.Close(ctx) }() // cleanup resources
+ for { // iterate over result sets
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ for { // iterate over rows
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ type myStruct struct {
+ id uint64 `sql:"id"`
+ str string `sql:"myStr"`
+ }
+ var s myStruct
+ if err = row.ScanStruct(&s); err != nil {
+ return err // generally scan error not retryable, return it for driver check error
+ }
+ }
+ }
+
+ return res.Err() // return finally result error for auto-retry with driver
+ },
+ query.WithIdempotent(),
+ )
+ if err != nil {
+ log.Printf("unexpected error: %v", err)
+ }
+}
+
//nolint:testableexamples, nonamedreturns
func Example_table() {
ctx := context.TODO()
diff --git a/examples/basic/native/README.md b/examples/basic/native/query/README.md
similarity index 100%
rename from examples/basic/native/README.md
rename to examples/basic/native/query/README.md
diff --git a/examples/basic/native/query/data.go b/examples/basic/native/query/data.go
new file mode 100644
index 000000000..cbdbde95c
--- /dev/null
+++ b/examples/basic/native/query/data.go
@@ -0,0 +1,208 @@
+package main
+
+import (
+ "time"
+
+ "github.com/google/uuid"
+ "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
+)
+
+func seriesData(id string, released time.Time, title, info, comment string) types.Value {
+ var commentv types.Value
+ if comment == "" {
+ commentv = types.NullValue(types.TypeUTF8)
+ } else {
+ commentv = types.OptionalValue(types.TextValue(comment))
+ }
+
+ return types.StructValue(
+ types.StructFieldValue("series_id", types.BytesValueFromString(id)),
+ types.StructFieldValue("release_date", types.DateValueFromTime(released)),
+ types.StructFieldValue("title", types.TextValue(title)),
+ types.StructFieldValue("series_info", types.TextValue(info)),
+ types.StructFieldValue("comment", commentv),
+ )
+}
+
+func seasonData(seriesID, seasonID, title string, first, last time.Time) types.Value {
+ return types.StructValue(
+ types.StructFieldValue("series_id", types.BytesValueFromString(seriesID)),
+ types.StructFieldValue("season_id", types.BytesValueFromString(seasonID)),
+ types.StructFieldValue("title", types.TextValue(title)),
+ types.StructFieldValue("first_aired", types.DateValueFromTime(first)),
+ types.StructFieldValue("last_aired", types.DateValueFromTime(last)),
+ )
+}
+
+func episodeData(seriesID, seasonID, episodeID, title string, date time.Time) types.Value {
+ return types.StructValue(
+ types.StructFieldValue("series_id", types.BytesValueFromString(seriesID)),
+ types.StructFieldValue("season_id", types.BytesValueFromString(seasonID)),
+ types.StructFieldValue("episode_id", types.BytesValueFromString(episodeID)),
+ types.StructFieldValue("title", types.TextValue(title)),
+ types.StructFieldValue("air_date", types.DateValueFromTime(date)),
+ )
+}
+
+func getData() (series, seasons, episodes []types.Value) {
+ for seriesID, fill := range map[string]func(seriesID string) (
+ seriesData types.Value, seasons []types.Value, episodes []types.Value,
+ ){
+ uuid.New().String(): getDataForITCrowd,
+ uuid.New().String(): getDataForSiliconValley,
+ } {
+ seriesData, seasonsData, episodesData := fill(seriesID)
+ series = append(series, seriesData)
+ seasons = append(seasons, seasonsData...)
+ episodes = append(episodes, episodesData...)
+ }
+
+ return
+}
+
+func getDataForITCrowd(seriesID string) (series types.Value, seasons, episodes []types.Value) {
+ series = seriesData(
+ seriesID, date("2006-02-03"), "IT Crowd", ""+
+ "The IT Crowd is a British sitcom produced by Channel 4, written by Graham Linehan, produced by "+
+ "Ash Atalla and starring Chris O'Dowd, Richard Ayoade, Katherine Parkinson, and Matt Berry.",
+ "", // NULL comment.
+ )
+ for _, season := range []struct { //nolint:gocritic
+ title string
+ first time.Time
+ last time.Time
+ episodes map[string]time.Time
+ }{
+ {"Season 1", date("2006-02-03"), date("2006-03-03"), map[string]time.Time{
+ "Yesterday's Jam": date("2006-02-03"),
+ "Calamity Jen": date("2006-02-03"),
+ "Fifty-Fifty": date("2006-02-10"),
+ "The Red Door": date("2006-02-17"),
+ "The Haunting of Bill Crouse": date("2006-02-24"),
+ "Aunt Irma Visits": date("2006-03-03"),
+ }},
+ {"Season 2", date("2007-08-24"), date("2007-09-28"), map[string]time.Time{
+ "The Work Outing": date("2006-08-24"),
+ "Return of the Golden Child": date("2007-08-31"),
+ "Moss and the German": date("2007-09-07"),
+ "The Dinner Party": date("2007-09-14"),
+ "Smoke and Mirrors": date("2007-09-21"),
+ "Men Without Women": date("2007-09-28"),
+ }},
+ {"Season 3", date("2008-11-21"), date("2008-12-26"), map[string]time.Time{
+ "From Hell": date("2008-11-21"),
+ "Are We Not Men?": date("2008-11-28"),
+ "Tramps Like Us": date("2008-12-05"),
+ "The Speech": date("2008-12-12"),
+ "Friendface": date("2008-12-19"),
+ "Calendar Geeks": date("2008-12-26"),
+ }},
+ {"Season 4", date("2010-06-25"), date("2010-07-30"), map[string]time.Time{
+ "Jen The Fredo": date("2010-06-25"),
+ "The Final Countdown": date("2010-07-02"),
+ "Something Happened": date("2010-07-09"),
+ "Italian For Beginners": date("2010-07-16"),
+ "Bad Boys": date("2010-07-23"),
+ "Reynholm vs Reynholm": date("2010-07-30"),
+ }},
+ } {
+ seasonID := uuid.New().String()
+ seasons = append(seasons, seasonData(seriesID, seasonID, season.title, season.first, season.last))
+ for title, date := range season.episodes {
+ episodes = append(episodes, episodeData(seriesID, seasonID, uuid.New().String(), title, date))
+ }
+ }
+
+ return series, seasons, episodes
+}
+
+func getDataForSiliconValley(seriesID string) (series types.Value, seasons, episodes []types.Value) {
+ series = seriesData(
+ seriesID, date("2014-04-06"), "Silicon Valley", ""+
+ "Silicon Valley is an American comedy television series created by Mike Judge, John Altschuler and "+
+ "Dave Krinsky. The series focuses on five young men who founded a startup company in Silicon Valley.",
+ "Some comment here",
+ )
+ for _, season := range []struct { //nolint:gocritic
+ title string
+ first time.Time
+ last time.Time
+ episodes map[string]time.Time
+ }{
+ {"Season 1", date("2006-02-03"), date("2006-03-03"), map[string]time.Time{
+ "Minimum Viable Product": date("2014-04-06"),
+ "The Cap Table": date("2014-04-13"),
+ "Articles of Incorporation": date("2014-04-20"),
+ "Fiduciary Duties": date("2014-04-27"),
+ "Signaling Risk": date("2014-05-04"),
+ "Third Party Insourcing": date("2014-05-11"),
+ "Proof of Concept": date("2014-05-18"),
+ "Optimal Tip-to-Tip Efficiency": date("2014-06-01"),
+ }},
+ {"Season 2", date("2007-08-24"), date("2007-09-28"), map[string]time.Time{
+ "Sand Hill Shuffle": date("2015-04-12"),
+ "Runaway Devaluation": date("2015-04-19"),
+ "Bad Money": date("2015-04-26"),
+ "The Lady": date("2015-05-03"),
+ "Server Space": date("2015-05-10"),
+ "Homicide": date("2015-05-17"),
+ "Adult Content": date("2015-05-24"),
+ "White Hat/Black Hat": date("2015-05-31"),
+ "Binding Arbitration": date("2015-06-07"),
+ "Two Days of the Condor": date("2015-06-14"),
+ }},
+ {"Season 3", date("2008-11-21"), date("2008-12-26"), map[string]time.Time{
+ "Founder Friendly": date("2016-04-24"),
+ "Two in the Box": date("2016-05-01"),
+ "Meinertzhagen's Haversack": date("2016-05-08"),
+ "Maleant Data Systems Solutions": date("2016-05-15"),
+ "The Empty Chair": date("2016-05-22"),
+ "Bachmanity Insanity": date("2016-05-29"),
+ "To Build a Better Beta": date("2016-06-05"),
+ "Bachman's Earnings Over-Ride": date("2016-06-12"),
+ "Daily Active Users": date("2016-06-19"),
+ "The Uptick": date("2016-06-26"),
+ }},
+ {"Season 4", date("2010-06-25"), date("2010-07-30"), map[string]time.Time{
+ "Success Failure": date("2017-04-23"),
+ "Terms of Service": date("2017-04-30"),
+ "Intellectual Property": date("2017-05-07"),
+ "Teambuilding Exercise": date("2017-05-14"),
+ "The Blood Boy": date("2017-05-21"),
+ "Customer Service": date("2017-05-28"),
+ "The Patent Troll": date("2017-06-04"),
+ "The Keenan Vortex": date("2017-06-11"),
+ "Hooli-Con": date("2017-06-18"),
+ "Server Error": date("2017-06-25"),
+ }},
+ {"Season 5", date("2018-03-25"), date("2018-05-13"), map[string]time.Time{
+ "Grow Fast or Die Slow": date("2018-03-25"),
+ "Reorientation": date("2018-04-01"),
+ "Chief Operating Officer": date("2018-04-08"),
+ "Tech Evangelist": date("2018-04-15"),
+ "Facial Recognition": date("2018-04-22"),
+ "Artificial Emotional Intelligence": date("2018-04-29"),
+ "Initial Coin Offering": date("2018-05-06"),
+ "Fifty-One Percent": date("2018-05-13"),
+ }},
+ } {
+ seasonID := uuid.New().String()
+ seasons = append(seasons, seasonData(seriesID, seasonID, season.title, season.first, season.last))
+ for title, date := range season.episodes {
+ episodes = append(episodes, episodeData(seriesID, seasonID, uuid.New().String(), title, date))
+ }
+ }
+
+ return series, seasons, episodes
+}
+
+const dateISO8601 = "2006-01-02"
+
+func date(date string) time.Time {
+ t, err := time.Parse(dateISO8601, date)
+ if err != nil {
+ panic(err)
+ }
+
+ return t
+}
diff --git a/examples/basic/native/query/main.go b/examples/basic/native/query/main.go
new file mode 100644
index 000000000..bf13c8f2f
--- /dev/null
+++ b/examples/basic/native/query/main.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path"
+ "strings"
+ "time"
+
+ environ "github.com/ydb-platform/ydb-go-sdk-auth-environ"
+ ydb "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/sugar"
+)
+
+func isYdbVersionHaveQueryService() error {
+ minYdbVersion := strings.Split("24.1", ".")
+ ydbVersion := strings.Split(os.Getenv("YDB_VERSION"), ".")
+ for i, component := range ydbVersion {
+ if i < len(minYdbVersion) {
+ if r := strings.Compare(component, minYdbVersion[i]); r < 0 {
+ return fmt.Errorf("example '%s' run on minimal YDB version '%v', but current version is '%s'",
+ os.Args[0],
+ strings.Join(minYdbVersion, "."),
+ func() string {
+ if len(ydbVersion) > 0 && ydbVersion[0] != "" {
+ return strings.Join(ydbVersion, ".")
+ }
+
+ return "undefined"
+ }(),
+ )
+ } else if r > 0 {
+ return nil
+ }
+ }
+ }
+
+ return nil
+}
+
+func main() {
+ if err := isYdbVersionHaveQueryService(); err != nil {
+ fmt.Println(err.Error())
+
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+
+ dsn, exists := os.LookupEnv("YDB_CONNECTION_STRING")
+ if !exists {
+ panic("YDB_CONNECTION_STRING environment variable not defined")
+ }
+
+ db, err := ydb.Open(ctx,
+ dsn,
+ environ.WithEnvironCredentials(ctx),
+ )
+ if err != nil {
+ panic(fmt.Errorf("connect error: %w", err))
+ }
+ defer func() { _ = db.Close(ctx) }()
+
+ prefix := path.Join(db.Name(), "native/query")
+
+ err = sugar.RemoveRecursive(ctx, db, prefix)
+ if err != nil {
+ panic(err)
+ }
+
+ err = createTables(ctx, db.Query(), prefix)
+ if err != nil {
+ panic(fmt.Errorf("create tables error: %w", err))
+ }
+
+ err = fillTablesWithData(ctx, db.Query(), prefix)
+ if err != nil {
+ panic(fmt.Errorf("fill tables with data error: %w", err))
+ }
+
+ err = read(ctx, db.Query(), prefix)
+ if err != nil {
+ panic(fmt.Errorf("select simple error: %w", err))
+ }
+}
diff --git a/examples/basic/native/query/series.go b/examples/basic/native/query/series.go
new file mode 100644
index 000000000..6c66d3912
--- /dev/null
+++ b/examples/basic/native/query/series.go
@@ -0,0 +1,202 @@
+package main
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "path"
+ "time"
+
+ ydb "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+)
+
+func read(ctx context.Context, c query.Client, prefix string) error {
+ return c.Do(ctx,
+ func(ctx context.Context, s query.Session) (err error) {
+ _, result, err := s.Execute(ctx, fmt.Sprintf(`
+ PRAGMA TablePathPrefix("%s");
+ DECLARE $seriesID AS Uint64;
+ SELECT
+ series_id,
+ title,
+ release_date
+ FROM
+ series
+ `, prefix),
+ query.WithTxControl(query.TxControl(query.BeginTx(query.WithOnlineReadOnly()))),
+ query.WithStatsMode(query.StatsModeBasic),
+ )
+ if err != nil {
+ return err
+ }
+
+ defer func() {
+ _ = result.Close(ctx)
+ }()
+
+ for {
+ resultSet, err := result.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ return result.Err()
+ }
+
+ return err
+ }
+ for {
+ row, err := resultSet.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ return result.Err()
+ }
+
+ return err
+ }
+
+ var info struct {
+ SeriesID string `sql:"series_id"`
+ Title string `sql:"title"`
+ ReleaseDate time.Time `sql:"release_date"`
+ }
+ err = row.ScanStruct(&info)
+ if err != nil {
+ return err
+ }
+ log.Printf("%+v", info)
+ }
+ }
+ },
+ )
+}
+
+func fillTablesWithData(ctx context.Context, c query.Client, prefix string) error {
+ series, seasons, episodes := getData()
+
+ return c.Do(ctx,
+ func(ctx context.Context, s query.Session) (err error) {
+ _, _, err = s.Execute(ctx,
+ fmt.Sprintf(`
+ PRAGMA TablePathPrefix("%s");
+
+ DECLARE $seriesData AS List>>;
+
+ DECLARE $seasonsData AS List>;
+
+ DECLARE $episodesData AS List>;
+
+ REPLACE INTO series
+ SELECT
+ series_id,
+ title,
+ series_info,
+ release_date,
+ comment
+ FROM AS_TABLE($seriesData);
+
+ REPLACE INTO seasons
+ SELECT
+ series_id,
+ season_id,
+ title,
+ first_aired,
+ last_aired
+ FROM AS_TABLE($seasonsData);
+
+ REPLACE INTO episodes
+ SELECT
+ series_id,
+ season_id,
+ episode_id,
+ title,
+ air_date
+ FROM AS_TABLE($episodesData);
+ `, prefix),
+ query.WithParameters(ydb.ParamsBuilder().
+ Param("$seriesData").BeginList().AddItems(series...).EndList().
+ Param("$seasonsData").BeginList().AddItems(seasons...).EndList().
+ Param("$episodesData").BeginList().AddItems(episodes...).EndList().
+ Build(),
+ ),
+ )
+
+ return err
+ },
+ )
+}
+
+func createTables(ctx context.Context, c query.Client, prefix string) error {
+ return c.Do(ctx,
+ func(ctx context.Context, s query.Session) error {
+ _, _, err := s.Execute(ctx, fmt.Sprintf(`
+ CREATE TABLE IF NOT EXISTS %s (
+ series_id Bytes,
+ title Text,
+ series_info Text,
+ release_date Date,
+ comment Text,
+
+ PRIMARY KEY(series_id)
+ )
+ `, "`"+path.Join(prefix, "series")+"`"),
+ query.WithTxControl(query.NoTx()),
+ )
+ if err != nil {
+ return err
+ }
+
+ _, _, err = s.Execute(ctx, fmt.Sprintf(`
+ CREATE TABLE IF NOT EXISTS %s (
+ series_id Bytes,
+ season_id Bytes,
+ title Text,
+ first_aired Date,
+ last_aired Date,
+
+ PRIMARY KEY(series_id,season_id)
+ )
+ `, "`"+path.Join(prefix, "seasons")+"`"),
+ query.WithTxControl(query.NoTx()),
+ )
+ if err != nil {
+ return err
+ }
+
+ _, _, err = s.Execute(ctx, fmt.Sprintf(`
+ CREATE TABLE IF NOT EXISTS %s (
+ series_id Bytes,
+ season_id Bytes,
+ episode_id Bytes,
+ title Text,
+ air_date Date,
+
+ PRIMARY KEY(series_id,season_id,episode_id)
+ )
+ `, "`"+path.Join(prefix, "episodes")+"`"),
+ query.WithTxControl(query.NoTx()),
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+ },
+ )
+}
diff --git a/examples/basic/native/table/README.md b/examples/basic/native/table/README.md
new file mode 100644
index 000000000..1f2543f35
--- /dev/null
+++ b/examples/basic/native/table/README.md
@@ -0,0 +1,8 @@
+# Basic example via native driver
+
+Basic example demonstrates the possibilities of `YDB`:
+ - create/drop/describe tables
+ - upsert data
+ - select with data query (request-response API)
+ - select with scan query (streaming API)
+ - read table (streaming API)
\ No newline at end of file
diff --git a/examples/basic/native/data.go b/examples/basic/native/table/data.go
similarity index 100%
rename from examples/basic/native/data.go
rename to examples/basic/native/table/data.go
diff --git a/examples/basic/native/main.go b/examples/basic/native/table/main.go
similarity index 97%
rename from examples/basic/native/main.go
rename to examples/basic/native/table/main.go
index 43be4ae9a..b624c4f12 100644
--- a/examples/basic/native/main.go
+++ b/examples/basic/native/table/main.go
@@ -30,7 +30,7 @@ func main() {
}
defer func() { _ = db.Close(ctx) }()
- prefix := path.Join(db.Name(), "native")
+ prefix := path.Join(db.Name(), "native/table")
err = sugar.RemoveRecursive(ctx, db, prefix)
if err != nil {
diff --git a/examples/basic/native/series.go b/examples/basic/native/table/series.go
similarity index 64%
rename from examples/basic/native/series.go
rename to examples/basic/native/table/series.go
index 625c5967e..2a5bc91b4 100644
--- a/examples/basic/native/series.go
+++ b/examples/basic/native/table/series.go
@@ -9,7 +9,6 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/result"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/named"
"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
@@ -70,128 +69,127 @@ SELECT
FROM AS_TABLE($episodesData);
`))
-func readTable(ctx context.Context, c table.Client, path string) (err error) {
- var res result.StreamResult
- err = c.Do(ctx,
- func(ctx context.Context, s table.Session) (err error) {
- res, err = s.StreamReadTable(ctx, path,
+func readTable(ctx context.Context, c table.Client, path string) error {
+ return c.Do(ctx,
+ func(ctx context.Context, s table.Session) error {
+ res, err := s.StreamReadTable(ctx, path,
options.ReadOrdered(),
options.ReadColumn("series_id"),
options.ReadColumn("title"),
options.ReadColumn("release_date"),
)
-
- return
- },
- )
- if err != nil {
- return err
- }
- defer func() {
- _ = res.Close()
- }()
- log.Printf("> read_table:")
- var (
- id *uint64
- title *string
- date *uint64
- )
- for res.NextResultSet(ctx) {
- for res.NextRow() {
- err = res.ScanNamed(
- named.Optional("series_id", &id),
- named.Optional("title", &title),
- named.Optional("release_date", &date),
- )
if err != nil {
return err
}
- log.Printf("# %d %s %d", *id, *title, *date)
- }
- }
- if err := res.Err(); err != nil {
- return err
- }
- if stats := res.Stats(); stats != nil {
- for i := 0; ; i++ {
- phase, ok := stats.NextPhase()
- if !ok {
- break
- }
- log.Printf(
- "# phase #%d: took %s",
- i, phase.Duration(),
+
+ defer func() {
+ _ = res.Close()
+ }()
+
+ log.Printf("> read_table:")
+
+ var (
+ id *uint64
+ title *string
+ date *uint64
)
- for {
- tbl, ok := phase.NextTableAccess()
- if !ok {
- break
+
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ err = res.ScanNamed(
+ named.Optional("series_id", &id),
+ named.Optional("title", &title),
+ named.Optional("release_date", &date),
+ )
+ if err != nil {
+ return err
+ }
+ log.Printf("# %d %s %d", *id, *title, *date)
+ }
+ }
+ if err := res.Err(); err != nil {
+ return err
+ }
+ if stats := res.Stats(); stats != nil {
+ for i := 0; ; i++ {
+ phase, ok := stats.NextPhase()
+ if !ok {
+ break
+ }
+ log.Printf(
+ "# phase #%d: took %s",
+ i, phase.Duration(),
+ )
+ for {
+ tbl, ok := phase.NextTableAccess()
+ if !ok {
+ break
+ }
+ log.Printf(
+ "# accessed %s: read=(%drows, %dbytes)",
+ tbl.Name, tbl.Reads.Rows, tbl.Reads.Bytes,
+ )
+ }
}
- log.Printf(
- "# accessed %s: read=(%drows, %dbytes)",
- tbl.Name, tbl.Reads.Rows, tbl.Reads.Bytes,
- )
}
- }
- }
- return res.Err()
+ return res.Err()
+ },
+ )
}
-func describeTableOptions(ctx context.Context, c table.Client) (err error) {
- var desc options.TableOptionsDescription
- err = c.Do(ctx,
+func describeTableOptions(ctx context.Context, c table.Client) error {
+ return c.Do(ctx,
func(ctx context.Context, s table.Session) (err error) {
- desc, err = s.DescribeTableOptions(ctx)
+ desc, err := s.DescribeTableOptions(ctx)
+ if err != nil {
+ return err
+ }
- return
- },
- )
- if err != nil {
- return err
- }
- log.Println("> describe_table_options:")
+ log.Println("> describe_table_options:")
- for i := range desc.TableProfilePresets {
- log.Printf("TableProfilePresets: %d/%d: %+v", i+1,
- len(desc.TableProfilePresets), desc.TableProfilePresets[i],
- )
- }
- for i := range desc.StoragePolicyPresets {
- log.Printf("StoragePolicyPresets: %d/%d: %+v", i+1,
- len(desc.StoragePolicyPresets), desc.StoragePolicyPresets[i],
- )
- }
- for i := range desc.CompactionPolicyPresets {
- log.Printf("CompactionPolicyPresets: %d/%d: %+v", i+1,
- len(desc.CompactionPolicyPresets), desc.CompactionPolicyPresets[i],
- )
- }
- for i := range desc.PartitioningPolicyPresets {
- log.Printf("PartitioningPolicyPresets: %d/%d: %+v", i+1,
- len(desc.PartitioningPolicyPresets), desc.PartitioningPolicyPresets[i],
- )
- }
- for i := range desc.ExecutionPolicyPresets {
- log.Printf("ExecutionPolicyPresets: %d/%d: %+v", i+1,
- len(desc.ExecutionPolicyPresets), desc.ExecutionPolicyPresets[i],
- )
- }
- for i := range desc.ReplicationPolicyPresets {
- log.Printf("ReplicationPolicyPresets: %d/%d: %+v", i+1,
- len(desc.ReplicationPolicyPresets), desc.ReplicationPolicyPresets[i],
- )
- }
- for i := range desc.CachingPolicyPresets {
- log.Printf("CachingPolicyPresets: %d/%d: %+v", i+1,
- len(desc.CachingPolicyPresets), desc.CachingPolicyPresets[i],
- )
- }
+ for i := range desc.TableProfilePresets {
+ log.Printf("TableProfilePresets: %d/%d: %+v", i+1,
+ len(desc.TableProfilePresets), desc.TableProfilePresets[i],
+ )
+ }
+ for i := range desc.StoragePolicyPresets {
+ log.Printf("StoragePolicyPresets: %d/%d: %+v", i+1,
+ len(desc.StoragePolicyPresets), desc.StoragePolicyPresets[i],
+ )
+ }
+ for i := range desc.CompactionPolicyPresets {
+ log.Printf("CompactionPolicyPresets: %d/%d: %+v", i+1,
+ len(desc.CompactionPolicyPresets), desc.CompactionPolicyPresets[i],
+ )
+ }
+ for i := range desc.PartitioningPolicyPresets {
+ log.Printf("PartitioningPolicyPresets: %d/%d: %+v", i+1,
+ len(desc.PartitioningPolicyPresets), desc.PartitioningPolicyPresets[i],
+ )
+ }
+ for i := range desc.ExecutionPolicyPresets {
+ log.Printf("ExecutionPolicyPresets: %d/%d: %+v", i+1,
+ len(desc.ExecutionPolicyPresets), desc.ExecutionPolicyPresets[i],
+ )
+ }
+ for i := range desc.ReplicationPolicyPresets {
+ log.Printf("ReplicationPolicyPresets: %d/%d: %+v", i+1,
+ len(desc.ReplicationPolicyPresets), desc.ReplicationPolicyPresets[i],
+ )
+ }
+ for i := range desc.CachingPolicyPresets {
+ log.Printf("CachingPolicyPresets: %d/%d: %+v", i+1,
+ len(desc.CachingPolicyPresets), desc.CachingPolicyPresets[i],
+ )
+ }
- return nil
+ return nil
+ },
+ )
}
-func selectSimple(ctx context.Context, c table.Client, prefix string) (err error) {
+func selectSimple(ctx context.Context, c table.Client, prefix string) error {
query := render(
template.Must(template.New("").Parse(`
PRAGMA TablePathPrefix("{{ .TablePathPrefix }}");
@@ -220,53 +218,51 @@ func selectSimple(ctx context.Context, c table.Client, prefix string) (err error
),
table.CommitTx(),
)
- var res result.Result
- err = c.Do(ctx,
- func(ctx context.Context, s table.Session) (err error) {
- _, res, err = s.Execute(ctx, readTx, query,
+
+ return c.Do(ctx,
+ func(ctx context.Context, s table.Session) error {
+ _, res, err := s.Execute(ctx, readTx, query,
table.NewQueryParameters(
table.ValueParam("$seriesID", types.Uint64Value(1)),
),
options.WithCollectStatsModeBasic(),
)
-
- return
- },
- )
- if err != nil {
- return err
- }
-
- defer func() {
- _ = res.Close()
- }()
-
- var (
- id *uint64
- title *string
- date *[]byte
- )
- for res.NextResultSet(ctx) {
- for res.NextRow() {
- err = res.ScanNamed(
- named.Optional("series_id", &id),
- named.Optional("title", &title),
- named.Optional("release_date", &date),
- )
if err != nil {
return err
}
- log.Printf(
- "> select_simple_transaction: %d %s %s",
- *id, *title, *date,
+
+ defer func() {
+ _ = res.Close()
+ }()
+
+ var (
+ id *uint64
+ title *string
+ date *[]byte
)
- }
- }
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ err = res.ScanNamed(
+ named.Optional("series_id", &id),
+ named.Optional("title", &title),
+ named.Optional("release_date", &date),
+ )
+ if err != nil {
+ return err
+ }
+ log.Printf(
+ "> select_simple_transaction: %d %s %s",
+ *id, *title, *date,
+ )
+ }
+ }
- return res.Err()
+ return res.Err()
+ },
+ )
}
-func scanQuerySelect(ctx context.Context, c table.Client, prefix string) (err error) {
+func scanQuerySelect(ctx context.Context, c table.Client, prefix string) error {
query := render(
template.Must(template.New("").Parse(`
PRAGMA TablePathPrefix("{{ .TablePathPrefix }}");
@@ -327,14 +323,15 @@ func scanQuerySelect(ctx context.Context, c table.Client, prefix string) (err er
)
}
-func fillTablesWithData(ctx context.Context, c table.Client, prefix string) (err error) {
+func fillTablesWithData(ctx context.Context, c table.Client, prefix string) error {
writeTx := table.TxControl(
table.BeginTx(
table.WithSerializableReadWrite(),
),
table.CommitTx(),
)
- err = c.Do(ctx,
+
+ return c.Do(ctx,
func(ctx context.Context, s table.Session) (err error) {
_, _, err = s.Execute(ctx, writeTx, render(fill, templateConfig{
TablePathPrefix: prefix,
@@ -347,14 +344,12 @@ func fillTablesWithData(ctx context.Context, c table.Client, prefix string) (err
return err
},
)
-
- return err
}
-func createTables(ctx context.Context, c table.Client, prefix string) (err error) {
- err = c.Do(ctx,
+func createTables(ctx context.Context, c table.Client, prefix string) error {
+ return c.Do(ctx,
func(ctx context.Context, s table.Session) error {
- return s.CreateTable(ctx, path.Join(prefix, "series"),
+ err := s.CreateTable(ctx, path.Join(prefix, "series"),
options.WithColumn("series_id", types.Optional(types.TypeUint64)),
options.WithColumn("title", types.Optional(types.TypeUTF8)),
options.WithColumn("series_info", types.Optional(types.TypeUTF8)),
@@ -362,15 +357,11 @@ func createTables(ctx context.Context, c table.Client, prefix string) (err error
options.WithColumn("comment", types.Optional(types.TypeUTF8)),
options.WithPrimaryKeyColumn("series_id"),
)
- },
- )
- if err != nil {
- return err
- }
+ if err != nil {
+ return err
+ }
- err = c.Do(ctx,
- func(ctx context.Context, s table.Session) error {
- return s.CreateTable(ctx, path.Join(prefix, "seasons"),
+ err = s.CreateTable(ctx, path.Join(prefix, "seasons"),
options.WithColumn("series_id", types.Optional(types.TypeUint64)),
options.WithColumn("season_id", types.Optional(types.TypeUint64)),
options.WithColumn("title", types.Optional(types.TypeUTF8)),
@@ -378,15 +369,11 @@ func createTables(ctx context.Context, c table.Client, prefix string) (err error
options.WithColumn("last_aired", types.Optional(types.TypeUint64)),
options.WithPrimaryKeyColumn("series_id", "season_id"),
)
- },
- )
- if err != nil {
- return err
- }
+ if err != nil {
+ return err
+ }
- err = c.Do(ctx,
- func(ctx context.Context, s table.Session) error {
- return s.CreateTable(ctx, path.Join(prefix, "episodes"),
+ err = s.CreateTable(ctx, path.Join(prefix, "episodes"),
options.WithColumn("series_id", types.Optional(types.TypeUint64)),
options.WithColumn("season_id", types.Optional(types.TypeUint64)),
options.WithColumn("episode_id", types.Optional(types.TypeUint64)),
@@ -394,17 +381,17 @@ func createTables(ctx context.Context, c table.Client, prefix string) (err error
options.WithColumn("air_date", types.Optional(types.TypeUint64)),
options.WithPrimaryKeyColumn("series_id", "season_id", "episode_id"),
)
+ if err != nil {
+ return err
+ }
+
+ return nil
},
)
- if err != nil {
- return err
- }
-
- return nil
}
-func describeTable(ctx context.Context, c table.Client, path string) (err error) {
- err = c.Do(ctx,
+func describeTable(ctx context.Context, c table.Client, path string) error {
+ return c.Do(ctx,
func(ctx context.Context, s table.Session) error {
desc, err := s.DescribeTable(ctx, path)
if err != nil {
@@ -418,8 +405,6 @@ func describeTable(ctx context.Context, c table.Client, path string) (err error)
return nil
},
)
-
- return
}
func render(t *template.Template, data interface{}) string {
diff --git a/examples/coordination/README.md b/examples/coordination/README.md
new file mode 100644
index 000000000..d5248aa99
--- /dev/null
+++ b/examples/coordination/README.md
@@ -0,0 +1,156 @@
+# Coordination Examples
+
+Coordination Examples demonstrate how to implement various distributed consistency primitives using the
+`coordination.Client` API.
+
+## Locks
+
+Strictly speaking, this is an implementation of a [lease](https://en.wikipedia.org/wiki/Lease_(computer_science)), not a
+lock in the traditional sense.
+
+Consider an application which is running a number of different instances. You want to ensure that some shared resource
+is accessed by only one instance at a time.
+
+The `lock` application is an example of that instance. When it starts, it waits until the distributed lock is acquired.
+When the application cannot consider the lock acquired anymore (for example, in the case of network issues) it stops
+working and tries to acquire the lock again.
+
+You may start any number of applications, only one of them will consider itself the holder of the lock at a time.
+
+Although we call it a lock, this mechanism **cannot be used to implement mutual exclusion by itself**. Session relies on
+physical time clocks. The server and the client clocks may be, and actually always are, out of sync. This results in a
+situation where for example the server releases the lease but the client still assumes it owns the lease. However,
+leases can be used as optimization in order to significantly reduce the possibility of resource contention.
+
+To start the application with the [YDB Docker database instance](https://ydb.tech/en/docs/getting_started/self_hosted/ydb_docker) run
+
+```bash
+$ go build
+$ YDB_ANONYMOUS_CREDENTIALS=1 ./lock -ydb grpc://localhost:2136/local --path /local/test --semaphore lock
+```
+
+When you stop the application which is currently holding the semaphore, another one should immediately acquire the lock
+and start doing its work.
+
+This example uses an ephemeral semaphore which is always acquired exclusive. The following pseudocode shows how it is
+used.
+
+```go
+for {
+ session, err := db.Coordination().OpenSession(ctx, path)
+ if err != nil {
+ // The context is canceled.
+ break
+ }
+
+ lease, err := session.AcquireSemaphore(ctx, semaphore, coordination.Exclusive, options.WithEphemeral(true))
+ if err != nil {
+ session.Close(ctx)
+ continue
+ }
+
+ // The lock is acquired.
+ go doWork(lease.Context())
+
+ // Wait until the lock is released.
+ <-lease.Context().Done():
+
+ // The lock is not acquired anymore.
+ cancelWork()
+}
+```
+
+## Workers
+
+This is an example of distributing long-running tasks among multiple workers. Consider there is a number of tasks that
+need to be processed simultaneously by a pool of workers. Each task is processed independently and the order of
+processing does not matter. A worker has a fixed capacity that defines how many tasks it is ready to process at a time.
+
+Any single task is associated with a Coordination Service semaphore. When the application starts, it grabs all task
+semaphores in order to acquire at least `capacity` of them. When it happens, it releases excessive ones and wait until
+the application finishes. Until then, it starts a new task for every acquired semaphore and waits until the number of
+running tasks becomes equal to the capacity of the worker.
+
+To start the application with the [YDB Docker database instance](https://ydb.tech/en/docs/getting_started/self_hosted/ydb_docker) run
+
+```bash
+$ go build
+$ YDB_ANONYMOUS_CREDENTIALS=1 ./lock -ydb grpc://localhost:2136/local -path /local/test --semaphore-prefix job- --tasks 10 --capacity 4
+```
+
+This example uses ephemeral semaphores which are always acquired exclusive. However, in a real application you may
+want to use persistent semaphores in order to store the state of workers in attached data. The following pseudocode
+shows how it is used.
+
+```go
+for {
+ session, err := db.Coordination().OpenSession(ctx, path)
+ if err != nil {
+ // The context is canceled.
+ break
+ }
+
+ semaphoreCtx, semaphoreCancel := context.WithCancel(ctx)
+ capacitySemaphore := semaphore.NewWeighted(capacity)
+ leaseChan := make(chan *coordination.Lease)
+ for _, name := range tasks {
+ go awaitSemaphore(semaphoreCtx, semaphoreCancel, session, name, capacitySemaphore, leaseChan)
+ }
+
+ tasksStarted := 0
+loop:
+ for {
+ lease := <-leaseChan
+
+ // Run a new task every time we acquire a semaphore.
+ go doWork(lease.Context())
+
+ tasksStarted++
+ if tasksStarted == capacity {
+ break
+ }
+ }
+
+ // The capacity is full, cancel all Acquire operations.
+ semaphoreCancel()
+
+ // Wait until the session is alive.
+ <-session.Context().Done()
+
+ // The tasks must be stopped since we do not own the semaphores anymore.
+ cancelTasks()
+}
+
+func awaitSemaphore(
+ ctx context.Context,
+ cancel context.CancelFunc,
+ session coordination.Session,
+ semaphoreName string,
+ capacitySemaphore *semaphore.Weighted,
+ leaseChan chan *coordination.Lease,
+) {
+ lease, err := session.AcquireSemaphore(
+ ctx,
+ semaphoreName,
+ coordination.Exclusive,
+ options.WithEphemeral(true),
+ )
+ if err != nil {
+ // Let the main loop know that something is wrong.
+ // ...
+ return
+ }
+
+ // If there is a need in tasks for the current worker, provide it with a new lease.
+ if capacitySemaphore.TryAcquire(1) {
+ leaseChan <- lease
+ } else {
+ // This may happen since we are waiting for all existing semaphores trying to grab the first available to us.
+ err := lease.Release()
+ if err != nil {
+ // Let the main loop know that something is wrong.
+ // ...
+ }
+ }
+}
+```
diff --git a/examples/coordination/lock/main.go b/examples/coordination/lock/main.go
new file mode 100644
index 000000000..905bb1e4b
--- /dev/null
+++ b/examples/coordination/lock/main.go
@@ -0,0 +1,144 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+
+ environ "github.com/ydb-platform/ydb-go-sdk-auth-environ"
+ ydb "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
+)
+
+var (
+ dsn string
+ path string
+ semaphore string
+)
+
+//nolint:gochecknoinits
+func init() {
+ required := []string{"ydb", "path", "semaphore"}
+ flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
+ flagSet.Usage = func() {
+ out := flagSet.Output()
+ _, _ = fmt.Fprintf(out, "Usage:\n%s [options]\n", os.Args[0])
+ _, _ = fmt.Fprintf(out, "\nOptions:\n")
+ flagSet.PrintDefaults()
+ }
+ flagSet.StringVar(&dsn,
+ "ydb", "",
+ "YDB connection string",
+ )
+ flagSet.StringVar(&path,
+ "path", "",
+ "coordination node path",
+ )
+ flagSet.StringVar(&semaphore,
+ "semaphore", "",
+ "semaphore name",
+ )
+ if err := flagSet.Parse(os.Args[1:]); err != nil {
+ flagSet.Usage()
+ os.Exit(1)
+ }
+ flagSet.Visit(func(f *flag.Flag) {
+ for i, arg := range required {
+ if arg == f.Name {
+ required = append(required[:i], required[i+1:]...)
+ }
+ }
+ })
+ if len(required) > 0 {
+ fmt.Printf("\nSome required options not defined: %v\n\n", required)
+ flagSet.Usage()
+ os.Exit(1)
+ }
+}
+
+func main() {
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
+ defer cancel()
+
+ db, err := ydb.Open(ctx, dsn,
+ environ.WithEnvironCredentials(ctx),
+ )
+ if err != nil {
+ panic(fmt.Errorf("connect error: %w", err))
+ }
+ defer func() { _ = db.Close(ctx) }()
+
+ err = db.Coordination().CreateNode(ctx, path, coordination.NodeConfig{
+ Path: "",
+ SelfCheckPeriodMillis: 1000,
+ SessionGracePeriodMillis: 1000,
+ ReadConsistencyMode: coordination.ConsistencyModeStrict,
+ AttachConsistencyMode: coordination.ConsistencyModeStrict,
+ RatelimiterCountersMode: coordination.RatelimiterCountersModeDetailed,
+ })
+ if err != nil {
+ fmt.Printf("failed to create coordination node: %v\n", err)
+
+ return
+ }
+
+ for {
+ fmt.Println("waiting for a lock...")
+
+ session, err := db.Coordination().Session(ctx, path)
+ if err != nil {
+ fmt.Println("failed to open session", err)
+
+ return
+ }
+
+ lease, err := session.AcquireSemaphore(ctx, semaphore, coordination.Exclusive, options.WithEphemeral(true))
+ if err != nil {
+ fmt.Printf("failed to acquire semaphore: %v\n", err)
+ _ = session.Close(ctx)
+
+ continue
+ }
+
+ fmt.Println("the lock is acquired")
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go doWork(lease.Context(), &wg)
+
+ select {
+ case <-ctx.Done():
+ fmt.Println("exiting")
+
+ return
+ case <-lease.Context().Done():
+ }
+
+ fmt.Println("the lock is released")
+ wg.Wait()
+ }
+}
+
+func doWork(ctx context.Context, wg *sync.WaitGroup) {
+ defer func() {
+ fmt.Println("suspending work")
+ wg.Done()
+ }()
+
+ fmt.Println("starting work")
+ for {
+ fmt.Println("work is in progress...")
+
+ select {
+ case <-ctx.Done():
+ return
+ case <-time.After(time.Second):
+ }
+ }
+}
diff --git a/examples/coordination/workers/main.go b/examples/coordination/workers/main.go
new file mode 100644
index 000000000..451336a7a
--- /dev/null
+++ b/examples/coordination/workers/main.go
@@ -0,0 +1,229 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "math/rand"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+
+ environ "github.com/ydb-platform/ydb-go-sdk-auth-environ"
+ ydb "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
+ "golang.org/x/sync/semaphore"
+)
+
+var (
+ dsn string
+ path string
+ semaphorePrefix string
+ taskCount int
+ capacity int
+)
+
+//nolint:gochecknoinits
+func init() {
+ required := []string{"ydb", "path"}
+ flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
+ flagSet.Usage = func() {
+ out := flagSet.Output()
+ _, _ = fmt.Fprintf(out, "Usage:\n%s [options]\n", os.Args[0])
+ _, _ = fmt.Fprintf(out, "\nOptions:\n")
+ flagSet.PrintDefaults()
+ }
+ flagSet.StringVar(&dsn,
+ "ydb", "",
+ "YDB connection string",
+ )
+ flagSet.StringVar(&path,
+ "path", "",
+ "coordination node path",
+ )
+ flagSet.StringVar(&semaphorePrefix,
+ "semaphore-prefix", "job-",
+ "semaphore prefix",
+ )
+ flagSet.IntVar(&taskCount,
+ "tasks", 10,
+ "the number of tasks",
+ )
+ flagSet.IntVar(&capacity,
+ "capacity", 4,
+ "the maximum number of tasks a worker can run",
+ )
+ if err := flagSet.Parse(os.Args[1:]); err != nil {
+ flagSet.Usage()
+ os.Exit(1)
+ }
+ flagSet.Visit(func(f *flag.Flag) {
+ for i, arg := range required {
+ if arg == f.Name {
+ required = append(required[:i], required[i+1:]...)
+ }
+ }
+ })
+ if len(required) > 0 {
+ fmt.Printf("\nSome required options not defined: %v\n\n", required)
+ flagSet.Usage()
+ os.Exit(1)
+ }
+}
+
+func main() {
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
+ defer cancel()
+
+ db, err := ydb.Open(ctx, dsn,
+ environ.WithEnvironCredentials(ctx),
+ )
+ if err != nil {
+ panic(fmt.Errorf("connect error: %w", err))
+ }
+ defer func() { _ = db.Close(ctx) }()
+
+ err = db.Coordination().CreateNode(ctx, path, coordination.NodeConfig{
+ Path: "",
+ SelfCheckPeriodMillis: 1000,
+ SessionGracePeriodMillis: 1000,
+ ReadConsistencyMode: coordination.ConsistencyModeStrict,
+ AttachConsistencyMode: coordination.ConsistencyModeStrict,
+ RatelimiterCountersMode: coordination.RatelimiterCountersModeDetailed,
+ })
+ if err != nil {
+ fmt.Printf("failed to create coordination node: %v\n", err)
+
+ return
+ }
+
+ tasks := make([]string, taskCount)
+ for i := 0; i < taskCount; i++ {
+ tasks[i] = fmt.Sprintf("%s%d", semaphorePrefix, i)
+ }
+ rand.Shuffle(taskCount, func(i int, j int) {
+ tasks[i], tasks[j] = tasks[j], tasks[i]
+ })
+
+ fmt.Println("starting tasks")
+ for {
+ session, err := db.Coordination().Session(ctx, path)
+ if err != nil {
+ fmt.Println("failed to open session", err)
+
+ return
+ }
+
+ semaphoreCtx, semaphoreCancel := context.WithCancel(ctx)
+ wg := sync.WaitGroup{}
+ wg.Add(taskCount)
+ leaseChan := make(chan *LeaseInfo)
+ sem := semaphore.NewWeighted(int64(capacity))
+
+ for _, name := range tasks {
+ go awaitSemaphore(semaphoreCtx, &wg, session, name, leaseChan, sem, semaphoreCancel)
+ }
+
+ tasksStarted := 0
+ loop:
+ for {
+ select {
+ case <-semaphoreCtx.Done():
+ break loop
+ case lease := <-leaseChan:
+ go doWork(lease.lease, lease.semaphoreName)
+ tasksStarted++
+ if tasksStarted == capacity {
+ break loop
+ }
+ case <-ctx.Done():
+ fmt.Println("exiting")
+
+ return
+ }
+ }
+
+ fmt.Println("all workers are started")
+
+ semaphoreCancel()
+ wg.Wait()
+
+ select {
+ case <-ctx.Done():
+ fmt.Println("exiting")
+
+ return
+ case <-session.Context().Done():
+ }
+ }
+}
+
+type LeaseInfo struct {
+ lease coordination.Lease
+ semaphoreName string
+}
+
+func awaitSemaphore(
+ ctx context.Context,
+ done *sync.WaitGroup,
+ session coordination.Session,
+ semaphoreName string,
+ leaseChan chan *LeaseInfo,
+ sem *semaphore.Weighted,
+ cancel context.CancelFunc,
+) {
+ defer done.Done()
+
+ lease, err := session.AcquireSemaphore(
+ ctx,
+ semaphoreName,
+ coordination.Exclusive,
+ options.WithEphemeral(true),
+ )
+ if err != nil {
+ if ctx.Err() != nil {
+ return
+ }
+
+ fmt.Println("failed to acquire semaphore", err)
+ cancel()
+
+ return
+ }
+
+ if sem.TryAcquire(1) {
+ leaseChan <- &LeaseInfo{lease: lease, semaphoreName: semaphoreName}
+ } else {
+ err := lease.Release()
+ if err != nil {
+ fmt.Println("failed to release semaphore", err)
+ cancel()
+ }
+ }
+}
+
+func doWork(
+ lease coordination.Lease,
+ name string,
+) {
+ fmt.Printf("worker %s: starting\n", name)
+
+ for {
+ select {
+ case <-lease.Context().Done():
+ fmt.Printf("worker %s: done\n", name)
+ err := lease.Release()
+ if err != nil {
+ fmt.Println("failed to release semaphore", err)
+ }
+
+ return
+ case <-time.After(time.Second):
+ }
+
+ fmt.Printf("worker %s: in progress\n", name)
+ }
+}
diff --git a/examples/go.mod b/examples/go.mod
index 1a74e556d..4e15e2a0b 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -9,9 +9,10 @@ require (
github.com/prometheus/client_golang v1.13.0
github.com/ydb-platform/gorm-driver v0.0.5
github.com/ydb-platform/ydb-go-sdk-auth-environ v0.1.2
- github.com/ydb-platform/ydb-go-sdk-prometheus v0.11.10
- github.com/ydb-platform/ydb-go-sdk/v3 v3.47.3
+ github.com/ydb-platform/ydb-go-sdk-prometheus/v2 v2.0.1
+ github.com/ydb-platform/ydb-go-sdk/v3 v3.54.0
github.com/ydb-platform/ydb-go-yc v0.10.1
+ golang.org/x/sync v0.3.0
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54
gorm.io/driver/postgres v1.5.0
gorm.io/driver/sqlite v1.5.0
@@ -32,7 +33,8 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
- github.com/jackc/pgx/v5 v5.3.0 // indirect
+ github.com/jackc/pgx/v5 v5.5.4 // indirect
+ github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
@@ -50,19 +52,17 @@ require (
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20220815090733-4c139c0154e2 // indirect
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf // indirect
- github.com/ydb-platform/ydb-go-sdk-metrics v0.16.3 // indirect
github.com/ydb-platform/ydb-go-yc-metadata v0.5.4 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.15.0 // indirect
- golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/grpc v1.57.1 // indirect
- google.golang.org/protobuf v1.31.0 // indirect
+ google.golang.org/protobuf v1.33.0 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.36.3 // indirect
modernc.org/ccgo/v3 v3.16.9 // indirect
diff --git a/examples/go.sum b/examples/go.sum
index 594e5ce12..79278f3b3 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -944,14 +944,17 @@ github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
-github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA=
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
+github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
+github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
+github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
+github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
@@ -1097,7 +1100,6 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -1126,7 +1128,8 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/rekby/fixenv v0.3.2/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
+github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM=
+github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -1194,16 +1197,12 @@ github.com/ydb-platform/gorm-driver v0.0.5 h1:q6Cg/iSFw4TAmSyMh25YM0GRmr6LVM2gnF
github.com/ydb-platform/gorm-driver v0.0.5/go.mod h1:fkCvWZlA3PzL5MiMc7yFOzxUOzLpY1uT8yZo+e4SV4Y=
github.com/ydb-platform/xorm v0.0.3 h1:MXk42lANB6r/MMLg/XdJfyXJycGUDlCeLiMlLGDKVPw=
github.com/ydb-platform/xorm v0.0.3/go.mod h1:hFsU7EUF0o3S+l5c0eyP2yPVjJ0d4gsFdqCsyazzwBc=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20240125100710-96fd3a874780 h1:E8Z7Zy/fKKDN4bYE1GvXX3DSjp9j7bPJy8Nnpe4Hxqg=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20240125100710-96fd3a874780/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf h1:ckwNHVo4bv2tqNkgx3W3HANh3ta1j6TR5qw08J1A7Tw=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
github.com/ydb-platform/ydb-go-sdk-auth-environ v0.1.2 h1:EYSI1kulnHb0H0zt3yOw4cRj4ABMSMGwNe43D+fX7e4=
github.com/ydb-platform/ydb-go-sdk-auth-environ v0.1.2/go.mod h1:Xfjce+VMU9yJVr1lj60yK2fFPWjB4jr/4cp3K7cjzi4=
-github.com/ydb-platform/ydb-go-sdk-metrics v0.16.3 h1:30D5jErLAiGjchVG2D9JiCLbST5LpAiyS7DoUtHkWsU=
-github.com/ydb-platform/ydb-go-sdk-metrics v0.16.3/go.mod h1:bqOjIBSt5LtA8fcTprRPGLvlQGkNlqBSRqnL+yZUJh4=
-github.com/ydb-platform/ydb-go-sdk-prometheus v0.11.10 h1:eXRJ8nKGv5Dyz7qTDFraahyqlSmOf1/8JqUtlxGlA4o=
-github.com/ydb-platform/ydb-go-sdk-prometheus v0.11.10/go.mod h1:7OffPa+OmsJgIP5G+2Cg5oP9+xB5UJSLm5AUpLxi5Uc=
+github.com/ydb-platform/ydb-go-sdk-prometheus/v2 v2.0.1 h1:Lsir3AC2VQOTlp8UjZY9zQdCVfWvBNHT3hZn+jSGoo0=
+github.com/ydb-platform/ydb-go-sdk-prometheus/v2 v2.0.1/go.mod h1:vofSH6XG0Cr04RV+V3fLp5apOhwDqj1kSoYD9/lmzmE=
github.com/ydb-platform/ydb-go-yc v0.8.3/go.mod h1:zUolAFGzJ5XG8uwiseTLr9Lapm7L7hdVdZgLSuv9FXE=
github.com/ydb-platform/ydb-go-yc v0.10.1 h1:9SBUpR94tzasEzqYSbBuuEp9mY/jV6xbwPMy3muvV7U=
github.com/ydb-platform/ydb-go-yc v0.10.1/go.mod h1:9HaZmOHUWy2MpJ4GZw9j9gR2I82/kb6H8fjsu8b2lxQ=
@@ -1240,8 +1239,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec h1:aB0WVMCyiVcqL1yMRLM4htiFlMvgdOml97GYnw9su5Q=
-go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
+go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
@@ -1955,8 +1954,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/examples/serverless/url_shortener/service.go b/examples/serverless/url_shortener/service.go
index b7a435458..041c55d9d 100644
--- a/examples/serverless/url_shortener/service.go
+++ b/examples/serverless/url_shortener/service.go
@@ -21,7 +21,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
environ "github.com/ydb-platform/ydb-go-sdk-auth-environ"
- ydbMetrics "github.com/ydb-platform/ydb-go-sdk-prometheus"
+ ydbMetrics "github.com/ydb-platform/ydb-go-sdk-prometheus/v2"
ydb "github.com/ydb-platform/ydb-go-sdk/v3"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
diff --git a/go.mod b/go.mod
index 396a84e87..339491ffc 100644
--- a/go.mod
+++ b/go.mod
@@ -10,14 +10,14 @@ require (
golang.org/x/sync v0.3.0
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/grpc v1.57.1
- google.golang.org/protobuf v1.31.0
+ google.golang.org/protobuf v1.33.0
)
// requires for tests only
require (
github.com/rekby/fixenv v0.6.1
github.com/stretchr/testify v1.7.1
- go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec // indirect
+ go.uber.org/mock v0.4.0
)
require (
diff --git a/go.sum b/go.sum
index 5ab333592..4cfd88ee1 100644
--- a/go.sum
+++ b/go.sum
@@ -24,8 +24,6 @@ github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kU
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
@@ -49,6 +47,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -58,8 +57,6 @@ github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUB
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/rekby/fixenv v0.3.2 h1:6AOdQ9Boaa/lOQJTY8GDmQRIhg3S3SD0mIEPkuDSkoQ=
-github.com/rekby/fixenv v0.3.2/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM=
github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -68,34 +65,25 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20231215113745-46f6d30f974a h1:9wx+kCrCQCdwmDe1AFW5yAHdzlo+RV7lcy6y7Zq661s=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20231215113745-46f6d30f974a/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20240125100710-96fd3a874780 h1:E8Z7Zy/fKKDN4bYE1GvXX3DSjp9j7bPJy8Nnpe4Hxqg=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20240125100710-96fd3a874780/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf h1:ckwNHVo4bv2tqNkgx3W3HANh3ta1j6TR5qw08J1A7Tw=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec h1:aB0WVMCyiVcqL1yMRLM4htiFlMvgdOml97GYnw9su5Q=
-go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
+go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -104,7 +92,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -112,13 +99,9 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
@@ -128,10 +111,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -165,8 +144,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/internal/allocator/allocator.go b/internal/allocator/allocator.go
index f87881079..c86a4f718 100644
--- a/internal/allocator/allocator.go
+++ b/internal/allocator/allocator.go
@@ -4,6 +4,7 @@ import (
"sync"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Table"
)
@@ -50,6 +51,15 @@ type (
tableQueryAllocator
tableQueryYqlTextAllocator
tableQueryIDAllocator
+ queryExecuteQueryRequestAllocator
+ queryExecuteQueryRequestQueryContentAllocator
+ queryExecuteQueryResponsePartAllocator
+ queryQueryContentAllocator
+ queryTransactionControlAllocator
+ queryTransactionControlBeginTxAllocator
+ queryTransactionControlTxIDAllocator
+ queryTransactionSettingsAllocator
+ queryTransactionSettingsSerializableReadWriteAllocator
}
)
@@ -99,6 +109,15 @@ func (a *Allocator) Free() {
a.tableQueryAllocator.free()
a.tableQueryYqlTextAllocator.free()
a.tableQueryIDAllocator.free()
+ a.queryExecuteQueryRequestAllocator.free()
+ a.queryExecuteQueryRequestQueryContentAllocator.free()
+ a.queryExecuteQueryResponsePartAllocator.free()
+ a.queryQueryContentAllocator.free()
+ a.queryTransactionControlAllocator.free()
+ a.queryTransactionControlBeginTxAllocator.free()
+ a.queryTransactionControlTxIDAllocator.free()
+ a.queryTransactionSettingsAllocator.free()
+ a.queryTransactionSettingsSerializableReadWriteAllocator.free()
allocatorPool.Put(a)
}
@@ -385,7 +404,7 @@ func (a *structAllocator) Struct() (v *Ydb.StructType) {
func (a *structAllocator) free() {
for _, v := range a.allocations {
- members := v.Members
+ members := v.GetMembers()
for i := range members {
members[i] = nil
}
@@ -447,7 +466,7 @@ func (a *tupleAllocator) Tuple() (v *Ydb.TupleType) {
func (a *tupleAllocator) free() {
for _, v := range a.allocations {
- elements := v.Elements
+ elements := v.GetElements()
for i := range elements {
elements[i] = nil
}
@@ -718,8 +737,8 @@ func (a *valueAllocator) Value() (v *Ydb.Value) {
func (a *valueAllocator) free() {
for _, v := range a.allocations {
- items := v.Items
- pairs := v.Pairs
+ items := v.GetItems()
+ pairs := v.GetPairs()
for i := range items {
items[i] = nil
}
@@ -902,6 +921,183 @@ func (a *tableQueryIDAllocator) free() {
a.allocations = a.allocations[:0]
}
+type queryExecuteQueryRequestAllocator struct {
+ allocations []*Ydb_Query.ExecuteQueryRequest
+}
+
+func (a *queryExecuteQueryRequestAllocator) QueryExecuteQueryRequest() (
+ v *Ydb_Query.ExecuteQueryRequest,
+) {
+ v = queryExecuteQueryRequestPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryExecuteQueryRequestAllocator) free() {
+ for _, v := range a.allocations {
+ v.Reset()
+ queryExecuteQueryRequestPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryExecuteQueryResponsePartAllocator struct {
+ allocations []*Ydb_Query.ExecuteQueryResponsePart
+}
+
+func (a *queryExecuteQueryResponsePartAllocator) QueryExecuteQueryResponsePart() (
+ v *Ydb_Query.ExecuteQueryResponsePart,
+) {
+ v = queryExecuteQueryResponsePartPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryExecuteQueryResponsePartAllocator) free() {
+ for _, v := range a.allocations {
+ v.Reset()
+ queryExecuteQueryResponsePartPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryExecuteQueryRequestQueryContentAllocator struct {
+ allocations []*Ydb_Query.ExecuteQueryRequest_QueryContent
+}
+
+func (a *queryExecuteQueryRequestQueryContentAllocator) QueryExecuteQueryRequestQueryContent() (
+ v *Ydb_Query.ExecuteQueryRequest_QueryContent,
+) {
+ v = queryExecuteQueryRequestQueryContentPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryExecuteQueryRequestQueryContentAllocator) free() {
+ for _, v := range a.allocations {
+ queryExecuteQueryRequestQueryContentPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryTransactionControlAllocator struct {
+ allocations []*Ydb_Query.TransactionControl
+}
+
+func (a *queryTransactionControlAllocator) QueryTransactionControl() (v *Ydb_Query.TransactionControl) {
+ v = queryTransactionControlPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryTransactionControlAllocator) free() {
+ for _, v := range a.allocations {
+ v.Reset()
+ queryTransactionControlPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryTransactionControlBeginTxAllocator struct {
+ allocations []*Ydb_Query.TransactionControl_BeginTx
+}
+
+func (a *queryTransactionControlBeginTxAllocator) QueryTransactionControlBeginTx() (
+ v *Ydb_Query.TransactionControl_BeginTx,
+) {
+ v = queryTransactionControlBeginTxPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryTransactionControlBeginTxAllocator) free() {
+ for _, v := range a.allocations {
+ queryTransactionControlBeginTxPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryTransactionControlTxIDAllocator struct {
+ allocations []*Ydb_Query.TransactionControl_TxId
+}
+
+func (a *queryTransactionControlTxIDAllocator) QueryTransactionControlTxID() (v *Ydb_Query.TransactionControl_TxId) {
+ v = queryTransactionControlTxIDPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryTransactionControlTxIDAllocator) free() {
+ for _, v := range a.allocations {
+ queryTransactionControlTxIDPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryTransactionSettingsAllocator struct {
+ allocations []*Ydb_Query.TransactionSettings
+}
+
+func (a *queryTransactionSettingsAllocator) QueryTransactionSettings() (v *Ydb_Query.TransactionSettings) {
+ v = queryTransactionSettingsPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryTransactionSettingsAllocator) free() {
+ for _, v := range a.allocations {
+ v.Reset()
+ queryTransactionSettingsPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryTransactionSettingsSerializableReadWriteAllocator struct {
+ allocations []*Ydb_Query.TransactionSettings_SerializableReadWrite
+}
+
+func (a *queryTransactionSettingsSerializableReadWriteAllocator) QueryTransactionSettingsSerializableReadWrite() (
+ v *Ydb_Query.TransactionSettings_SerializableReadWrite,
+) {
+ v = queryTransactionSettingsSerializableReadWritePool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryTransactionSettingsSerializableReadWriteAllocator) free() {
+ for _, v := range a.allocations {
+ queryTransactionSettingsSerializableReadWritePool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
+type queryQueryContentAllocator struct {
+ allocations []*Ydb_Query.QueryContent
+}
+
+func (a *queryQueryContentAllocator) QueryQueryContent() (v *Ydb_Query.QueryContent) {
+ v = queryQueryContentPool.Get()
+ a.allocations = append(a.allocations, v)
+
+ return v
+}
+
+func (a *queryQueryContentAllocator) free() {
+ for _, v := range a.allocations {
+ v.Reset()
+ queryQueryContentPool.Put(v)
+ }
+ a.allocations = a.allocations[:0]
+}
+
type Pool[T any] sync.Pool
func (p *Pool[T]) Get() *T {
@@ -919,46 +1115,55 @@ func (p *Pool[T]) Put(t *T) {
}
var (
- allocatorPool Pool[Allocator]
- valuePool Pool[Ydb.Value]
- typePool Pool[Ydb.Type]
- typeDecimalPool Pool[Ydb.Type_DecimalType]
- typeListPool Pool[Ydb.Type_ListType]
- typeEmptyListPool Pool[Ydb.Type_EmptyListType]
- typeEmptyDictPool Pool[Ydb.Type_EmptyDictType]
- typeTuplePool Pool[Ydb.Type_TupleType]
- typeStructPool Pool[Ydb.Type_StructType]
- typeDictPool Pool[Ydb.Type_DictType]
- typeVariantPool Pool[Ydb.Type_VariantType]
- decimalPool Pool[Ydb.DecimalType]
- listPool Pool[Ydb.ListType]
- tuplePool Pool[Ydb.TupleType]
- structPool Pool[Ydb.StructType]
- dictPool Pool[Ydb.DictType]
- variantPool Pool[Ydb.VariantType]
- variantTupleItemsPool Pool[Ydb.VariantType_TupleItems]
- variantStructItemsPool Pool[Ydb.VariantType_StructItems]
- structMemberPool Pool[Ydb.StructMember]
- typeOptionalPool Pool[Ydb.Type_OptionalType]
- optionalPool Pool[Ydb.OptionalType]
- typedValuePool Pool[Ydb.TypedValue]
- boolPool Pool[Ydb.Value_BoolValue]
- bytesPool Pool[Ydb.Value_BytesValue]
- textPool Pool[Ydb.Value_TextValue]
- int32Pool Pool[Ydb.Value_Int32Value]
- uint32Pool Pool[Ydb.Value_Uint32Value]
- low128Pool Pool[Ydb.Value_Low_128]
- int64Pool Pool[Ydb.Value_Int64Value]
- uint64Pool Pool[Ydb.Value_Uint64Value]
- floatPool Pool[Ydb.Value_FloatValue]
- doublePool Pool[Ydb.Value_DoubleValue]
- nestedPool Pool[Ydb.Value_NestedValue]
- nullFlagPool Pool[Ydb.Value_NullFlagValue]
- pairPool Pool[Ydb.ValuePair]
- tableExecuteQueryResultPool Pool[Ydb_Table.ExecuteQueryResult]
- tableExecuteDataQueryRequestPool Pool[Ydb_Table.ExecuteDataQueryRequest]
- tableQueryCachePolicyPool Pool[Ydb_Table.QueryCachePolicy]
- tableQueryPool Pool[Ydb_Table.Query]
- tableQueryYqlTextPool Pool[Ydb_Table.Query_YqlText]
- tableQueryIDPool Pool[Ydb_Table.Query_Id]
+ allocatorPool Pool[Allocator]
+ valuePool Pool[Ydb.Value]
+ typePool Pool[Ydb.Type]
+ typeDecimalPool Pool[Ydb.Type_DecimalType]
+ typeListPool Pool[Ydb.Type_ListType]
+ typeEmptyListPool Pool[Ydb.Type_EmptyListType]
+ typeEmptyDictPool Pool[Ydb.Type_EmptyDictType]
+ typeTuplePool Pool[Ydb.Type_TupleType]
+ typeStructPool Pool[Ydb.Type_StructType]
+ typeDictPool Pool[Ydb.Type_DictType]
+ typeVariantPool Pool[Ydb.Type_VariantType]
+ decimalPool Pool[Ydb.DecimalType]
+ listPool Pool[Ydb.ListType]
+ tuplePool Pool[Ydb.TupleType]
+ structPool Pool[Ydb.StructType]
+ dictPool Pool[Ydb.DictType]
+ variantPool Pool[Ydb.VariantType]
+ variantTupleItemsPool Pool[Ydb.VariantType_TupleItems]
+ variantStructItemsPool Pool[Ydb.VariantType_StructItems]
+ structMemberPool Pool[Ydb.StructMember]
+ typeOptionalPool Pool[Ydb.Type_OptionalType]
+ optionalPool Pool[Ydb.OptionalType]
+ typedValuePool Pool[Ydb.TypedValue]
+ boolPool Pool[Ydb.Value_BoolValue]
+ bytesPool Pool[Ydb.Value_BytesValue]
+ textPool Pool[Ydb.Value_TextValue]
+ int32Pool Pool[Ydb.Value_Int32Value]
+ uint32Pool Pool[Ydb.Value_Uint32Value]
+ low128Pool Pool[Ydb.Value_Low_128]
+ int64Pool Pool[Ydb.Value_Int64Value]
+ uint64Pool Pool[Ydb.Value_Uint64Value]
+ floatPool Pool[Ydb.Value_FloatValue]
+ doublePool Pool[Ydb.Value_DoubleValue]
+ nestedPool Pool[Ydb.Value_NestedValue]
+ nullFlagPool Pool[Ydb.Value_NullFlagValue]
+ pairPool Pool[Ydb.ValuePair]
+ tableExecuteQueryResultPool Pool[Ydb_Table.ExecuteQueryResult]
+ tableExecuteDataQueryRequestPool Pool[Ydb_Table.ExecuteDataQueryRequest]
+ tableQueryCachePolicyPool Pool[Ydb_Table.QueryCachePolicy]
+ tableQueryPool Pool[Ydb_Table.Query]
+ tableQueryYqlTextPool Pool[Ydb_Table.Query_YqlText]
+ tableQueryIDPool Pool[Ydb_Table.Query_Id]
+ queryExecuteQueryRequestPool Pool[Ydb_Query.ExecuteQueryRequest]
+ queryExecuteQueryRequestQueryContentPool Pool[Ydb_Query.ExecuteQueryRequest_QueryContent]
+ queryExecuteQueryResponsePartPool Pool[Ydb_Query.ExecuteQueryResponsePart]
+ queryQueryContentPool Pool[Ydb_Query.QueryContent]
+ queryTransactionControlPool Pool[Ydb_Query.TransactionControl]
+ queryTransactionControlBeginTxPool Pool[Ydb_Query.TransactionControl_BeginTx]
+ queryTransactionControlTxIDPool Pool[Ydb_Query.TransactionControl_TxId]
+ queryTransactionSettingsPool Pool[Ydb_Query.TransactionSettings]
+ queryTransactionSettingsSerializableReadWritePool Pool[Ydb_Query.TransactionSettings_SerializableReadWrite]
)
diff --git a/internal/background/worker.go b/internal/background/worker.go
index 5f1e90a9a..91c0d1686 100644
--- a/internal/background/worker.go
+++ b/internal/background/worker.go
@@ -19,19 +19,15 @@ var (
// A Worker must not be copied after first use
type Worker struct {
- ctx context.Context
- workers sync.WaitGroup
- onceInit sync.Once
-
+ ctx context.Context
+ workers sync.WaitGroup
+ closeReason error
tasksCompleted empty.Chan
-
- m xsync.Mutex
-
- tasks chan backgroundTask
-
- closed bool
- stop context.CancelFunc
- closeReason error
+ tasks chan backgroundTask
+ stop context.CancelFunc
+ onceInit sync.Once
+ m xsync.Mutex
+ closed bool
}
type CallbackFunc func(ctx context.Context)
diff --git a/internal/backoff/backoff.go b/internal/backoff/backoff.go
index 6069a4204..d4b9cceeb 100644
--- a/internal/backoff/backoff.go
+++ b/internal/backoff/backoff.go
@@ -86,9 +86,9 @@ func New(opts ...option) logBackoff {
b := logBackoff{
r: xrand.New(xrand.WithLock()),
}
- for _, o := range opts {
- if o != nil {
- o(&b)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&b)
}
}
diff --git a/internal/balancer/balancer.go b/internal/balancer/balancer.go
index 2ee0140e6..f69ec11e2 100644
--- a/internal/balancer/balancer.go
+++ b/internal/balancer/balancer.go
@@ -97,7 +97,8 @@ func (b *Balancer) clusterDiscoveryAttempt(ctx context.Context) (err error) {
address = "ydb:///" + b.driverConfig.Endpoint()
onDone = trace.DriverOnBalancerClusterDiscoveryAttempt(
b.driverConfig.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID(
+ "github.com/ydb-platform/ydb-go-sdk/3/internal/balancer.(*Balancer).clusterDiscoveryAttempt"),
address,
)
endpoints []endpoint.Endpoint
@@ -173,14 +174,15 @@ func (b *Balancer) applyDiscoveredEndpoints(ctx context.Context, endpoints []end
var (
onDone = trace.DriverOnBalancerUpdate(
b.driverConfig.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID(
+ "github.com/ydb-platform/ydb-go-sdk/3/internal/balancer.(*Balancer).applyDiscoveredEndpoints"),
b.config.DetectLocalDC,
)
previousConns []conn.Conn
)
defer func() {
nodes, added, dropped := endpointsDiff(endpoints, previousConns)
- onDone(nodes, added, dropped, localDC, nil)
+ onDone(nodes, added, dropped, localDC)
}()
connections := endpointsToConnections(b.pool, endpoints)
@@ -211,7 +213,7 @@ func (b *Balancer) applyDiscoveredEndpoints(ctx context.Context, endpoints []end
func (b *Balancer) Close(ctx context.Context) (err error) {
onDone := trace.DriverOnBalancerClose(
b.driverConfig.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/balancer.(*Balancer).Close"),
)
defer func() {
onDone(err)
@@ -237,7 +239,7 @@ func New(
var (
onDone = trace.DriverOnBalancerInit(
driverConfig.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/balancer.New"),
driverConfig.Balancer().String(),
)
discoveryConfig = discoveryConfig.New(append(opts,
@@ -257,12 +259,9 @@ func New(
pool: pool,
localDCDetector: detectLocalDC,
}
- d, err := internalDiscovery.New(ctx, pool.Get(
+ d := internalDiscovery.New(ctx, pool.Get(
endpoint.New(driverConfig.Endpoint()),
), discoveryConfig)
- if err != nil {
- return nil, err
- }
b.discoveryClient = d
@@ -283,7 +282,7 @@ func New(
}
// run background discovering
if d := discoveryConfig.Interval(); d > 0 {
- b.discoveryRepeater = repeater.New(xcontext.WithoutDeadline(ctx),
+ b.discoveryRepeater = repeater.New(xcontext.ValueOnly(ctx),
d, b.clusterDiscoveryAttempt,
repeater.WithName("discovery"),
repeater.WithTrace(b.driverConfig.Trace()),
@@ -374,7 +373,7 @@ func (b *Balancer) connections() *connectionsState {
func (b *Balancer) getConn(ctx context.Context) (c conn.Conn, err error) {
onDone := trace.DriverOnBalancerChooseEndpoint(
b.driverConfig.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/balancer.(*Balancer).getConn"),
)
defer func() {
if err == nil {
diff --git a/internal/bind/bind.go b/internal/bind/bind.go
index 2b4f0a346..c6d1111c6 100644
--- a/internal/bind/bind.go
+++ b/internal/bind/bind.go
@@ -3,9 +3,9 @@ package bind
import (
"sort"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
- "github.com/ydb-platform/ydb-go-sdk/v3/table"
)
type blockID int
@@ -27,34 +27,34 @@ type Bind interface {
type Bindings []Bind
func (bindings Bindings) RewriteQuery(query string, args ...interface{}) (
- yql string, _ *table.QueryParameters, err error,
+ yql string, parameters []*params.Parameter, err error,
) {
if len(bindings) == 0 {
- var params []table.ParameterOption
- params, err = Params(args...)
+ parameters, err = Params(args...)
if err != nil {
return "", nil, xerrors.WithStackTrace(err)
}
- return query, table.NewQueryParameters(params...), nil
+ return query, parameters, nil
}
buffer := xstring.Buffer()
defer buffer.Free()
for i := range bindings {
- query, args, err = bindings[len(bindings)-1-i].RewriteQuery(query, args...)
- if err != nil {
- return "", nil, xerrors.WithStackTrace(err)
+ var e error
+ query, args, e = bindings[len(bindings)-1-i].RewriteQuery(query, args...)
+ if e != nil {
+ return "", nil, xerrors.WithStackTrace(e)
}
}
- params, err := Params(args...)
+ parameters, err = Params(args...)
if err != nil {
return "", nil, xerrors.WithStackTrace(err)
}
- return query, table.NewQueryParameters(params...), nil
+ return query, parameters, nil
}
func Sort(bindings []Bind) []Bind {
diff --git a/internal/bind/numeric_args.go b/internal/bind/numeric_args.go
index 89c9c4ecc..fac536068 100644
--- a/internal/bind/numeric_args.go
+++ b/internal/bind/numeric_args.go
@@ -5,6 +5,7 @@ import (
"strconv"
"unicode/utf8"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
@@ -16,9 +17,7 @@ func (m NumericArgs) blockID() blockID {
return blockYQL
}
-func (m NumericArgs) RewriteQuery(sql string, args ...interface{}) (
- yql string, newArgs []interface{}, err error,
-) {
+func (m NumericArgs) RewriteQuery(sql string, args ...interface{}) (yql string, newArgs []interface{}, err error) {
l := &sqlLexer{
src: sql,
stateFn: numericArgsStateFn,
@@ -29,14 +28,18 @@ func (m NumericArgs) RewriteQuery(sql string, args ...interface{}) (
l.stateFn = l.stateFn(l)
}
- var (
- buffer = xstring.Buffer()
- param table.ParameterOption
- )
+ buffer := xstring.Buffer()
defer buffer.Free()
if len(args) > 0 {
- newArgs = make([]interface{}, len(args))
+ parameters, err := parsePositionalParameters(args)
+ if err != nil {
+ return "", nil, err
+ }
+ newArgs = make([]interface{}, len(parameters))
+ for i, param := range parameters {
+ newArgs[i] = param
+ }
}
for _, p := range l.parts {
@@ -49,38 +52,20 @@ func (m NumericArgs) RewriteQuery(sql string, args ...interface{}) (
}
if int(p) > len(args) {
return "", nil, xerrors.WithStackTrace(
- fmt.Errorf("%w: $p%d, len(args) = %d", ErrInconsistentArgs, p, len(args)),
+ fmt.Errorf("%w: $%d, len(args) = %d", ErrInconsistentArgs, p, len(args)),
)
}
- paramName := "$p" + strconv.Itoa(int(p-1)) //nolint:goconst
- if newArgs[p-1] == nil {
- param, err = toYdbParam(paramName, args[p-1])
- if err != nil {
- return "", nil, xerrors.WithStackTrace(err)
- }
- newArgs[p-1] = param
- buffer.WriteString(param.Name())
- } else {
- buffer.WriteString(newArgs[p-1].(table.ParameterOption).Name())
- }
- }
- }
-
- for i, p := range newArgs {
- if p == nil {
- return "", nil, xerrors.WithStackTrace(
- fmt.Errorf("%w: $p%d, len(args) = %d", ErrInconsistentArgs, i+1, len(args)),
- )
+ paramIndex := int(p - 1)
+ buffer.WriteString(newArgs[paramIndex].(table.ParameterOption).Name())
}
}
+ yql = buffer.String()
if len(newArgs) > 0 {
- const prefix = "-- origin query with numeric args replacement\n"
-
- return prefix + buffer.String(), newArgs, nil
+ yql = "-- origin query with numeric args replacement\n" + yql
}
- return buffer.String(), newArgs, nil
+ return yql, newArgs, nil
}
func numericArgsStateFn(l *sqlLexer) stateFn {
@@ -130,6 +115,20 @@ func numericArgsStateFn(l *sqlLexer) stateFn {
}
}
+func parsePositionalParameters(args []interface{}) ([]*params.Parameter, error) {
+ newArgs := make([]*params.Parameter, len(args))
+ for i, arg := range args {
+ paramName := fmt.Sprintf("$p%d", i)
+ param, err := toYdbParam(paramName, arg)
+ if err != nil {
+ return nil, err
+ }
+ newArgs[i] = param
+ }
+
+ return newArgs, nil
+}
+
func numericArgState(l *sqlLexer) stateFn {
numbers := ""
defer func() {
diff --git a/internal/bind/params.go b/internal/bind/params.go
index a817dd2cb..95653b996 100644
--- a/internal/bind/params.go
+++ b/internal/bind/params.go
@@ -9,9 +9,9 @@ import (
"sort"
"time"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
- "github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
@@ -21,7 +21,7 @@ var (
errMultipleQueryParameters = errors.New("only one query arg *table.QueryParameters allowed")
)
-//nolint:gocyclo
+//nolint:gocyclo,funlen
func toValue(v interface{}) (_ types.Value, err error) {
if valuer, ok := v.(driver.Valuer); ok {
v, err = valuer.Value()
@@ -142,7 +142,7 @@ func supportNewTypeLink(x interface{}) string {
return "https://github.com/ydb-platform/ydb-go-sdk/issues/new?" + v.Encode()
}
-func toYdbParam(name string, value interface{}) (table.ParameterOption, error) {
+func toYdbParam(name string, value interface{}) (*params.Parameter, error) {
if na, ok := value.(driver.NamedValue); ok {
n, v := na.Name, na.Value
if n != "" {
@@ -157,7 +157,7 @@ func toYdbParam(name string, value interface{}) (table.ParameterOption, error) {
}
value = v
}
- if v, ok := value.(table.ParameterOption); ok {
+ if v, ok := value.(*params.Parameter); ok {
return v, nil
}
v, err := toValue(value)
@@ -171,69 +171,73 @@ func toYdbParam(name string, value interface{}) (table.ParameterOption, error) {
name = "$" + name
}
- return table.ValueParam(name, v), nil
+ return params.Named(name, v), nil
}
-func Params(args ...interface{}) (params []table.ParameterOption, _ error) {
- params = make([]table.ParameterOption, 0, len(args))
+func Params(args ...interface{}) ([]*params.Parameter, error) {
+ parameters := make([]*params.Parameter, 0, len(args))
for i, arg := range args {
+ var newParam *params.Parameter
+ var newParams []*params.Parameter
+ var err error
switch x := arg.(type) {
case driver.NamedValue:
- if x.Name == "" {
- switch xx := x.Value.(type) {
- case *table.QueryParameters:
- if len(args) > 1 {
- return nil, xerrors.WithStackTrace(errMultipleQueryParameters)
- }
- xx.Each(func(name string, v types.Value) {
- params = append(params, table.ValueParam(name, v))
- })
- case table.ParameterOption:
- params = append(params, xx)
- default:
- x.Name = fmt.Sprintf("$p%d", i)
- param, err := toYdbParam(x.Name, x.Value)
- if err != nil {
- return nil, xerrors.WithStackTrace(err)
- }
- params = append(params, param)
- }
- } else {
- param, err := toYdbParam(x.Name, x.Value)
- if err != nil {
- return nil, xerrors.WithStackTrace(err)
- }
- params = append(params, param)
- }
+ newParams, err = paramHandleNamedValue(x, i, len(args))
case sql.NamedArg:
if x.Name == "" {
return nil, xerrors.WithStackTrace(errUnnamedParam)
}
- param, err := toYdbParam(x.Name, x.Value)
- if err != nil {
- return nil, xerrors.WithStackTrace(err)
- }
- params = append(params, param)
- case *table.QueryParameters:
+ newParam, err = toYdbParam(x.Name, x.Value)
+ newParams = append(newParams, newParam)
+ case *params.Parameters:
if len(args) > 1 {
return nil, xerrors.WithStackTrace(errMultipleQueryParameters)
}
- x.Each(func(name string, v types.Value) {
- params = append(params, table.ValueParam(name, v))
- })
- case table.ParameterOption:
- params = append(params, x)
+ parameters = *x
+ case *params.Parameter:
+ newParams = append(newParams, x)
default:
- param, err := toYdbParam(fmt.Sprintf("$p%d", i), x)
+ newParam, err = toYdbParam(fmt.Sprintf("$p%d", i), x)
+ newParams = append(newParams, newParam)
+ }
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+ parameters = append(parameters, newParams...)
+ }
+ sort.Slice(parameters, func(i, j int) bool {
+ return parameters[i].Name() < parameters[j].Name()
+ })
+
+ return parameters, nil
+}
+
+func paramHandleNamedValue(arg driver.NamedValue, paramNumber, argsLen int) ([]*params.Parameter, error) {
+ if arg.Name == "" {
+ switch x := arg.Value.(type) {
+ case *params.Parameters:
+ if argsLen > 1 {
+ return nil, xerrors.WithStackTrace(errMultipleQueryParameters)
+ }
+
+ return *x, nil
+ case *params.Parameter:
+ return []*params.Parameter{x}, nil
+ default:
+ arg.Name = fmt.Sprintf("$p%d", paramNumber)
+ param, err := toYdbParam(arg.Name, arg.Value)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
- params = append(params, param)
+
+ return []*params.Parameter{param}, nil
+ }
+ } else {
+ param, err := toYdbParam(arg.Name, arg.Value)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
}
- }
- sort.Slice(params, func(i, j int) bool {
- return params[i].Name() < params[j].Name()
- })
- return params, nil
+ return []*params.Parameter{param}, nil
+ }
}
diff --git a/internal/bind/params_test.go b/internal/bind/params_test.go
index 80d47b4a3..d715c07a1 100644
--- a/internal/bind/params_test.go
+++ b/internal/bind/params_test.go
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
@@ -346,27 +347,27 @@ func named(name string, value interface{}) driver.NamedValue {
func TestYdbParam(t *testing.T) {
for _, tt := range []struct {
src interface{}
- dst table.ParameterOption
+ dst *params.Parameter
err error
}{
{
- src: table.ValueParam("$a", types.Int32Value(42)),
- dst: table.ValueParam("$a", types.Int32Value(42)),
+ src: params.Named("$a", types.Int32Value(42)),
+ dst: params.Named("$a", types.Int32Value(42)),
err: nil,
},
{
src: named("a", int(42)),
- dst: table.ValueParam("$a", types.Int32Value(42)),
+ dst: params.Named("$a", types.Int32Value(42)),
err: nil,
},
{
src: named("$a", int(42)),
- dst: table.ValueParam("$a", types.Int32Value(42)),
+ dst: params.Named("$a", types.Int32Value(42)),
err: nil,
},
{
src: named("a", uint(42)),
- dst: table.ValueParam("$a", types.Uint32Value(42)),
+ dst: params.Named("$a", types.Uint32Value(42)),
err: nil,
},
{
@@ -389,50 +390,50 @@ func TestYdbParam(t *testing.T) {
func TestArgsToParams(t *testing.T) {
for _, tt := range []struct {
args []interface{}
- params []table.ParameterOption
+ params []*params.Parameter
err error
}{
{
args: []interface{}{},
- params: []table.ParameterOption{},
+ params: []*params.Parameter{},
err: nil,
},
{
args: []interface{}{
1, uint64(2), "3",
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
{
args: []interface{}{
table.NewQueryParameters(
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
),
table.NewQueryParameters(
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
),
},
err: errMultipleQueryParameters,
},
{
args: []interface{}{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
@@ -442,10 +443,10 @@ func TestArgsToParams(t *testing.T) {
sql.Named("$p1", types.Uint64Value(2)),
sql.Named("$p2", types.TextValue("3")),
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
@@ -455,23 +456,23 @@ func TestArgsToParams(t *testing.T) {
driver.NamedValue{Name: "$p1", Value: types.Uint64Value(2)},
driver.NamedValue{Name: "$p2", Value: types.TextValue("3")},
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
{
args: []interface{}{
- driver.NamedValue{Value: table.ValueParam("$p0", types.Int32Value(1))},
- driver.NamedValue{Value: table.ValueParam("$p1", types.Uint64Value(2))},
- driver.NamedValue{Value: table.ValueParam("$p2", types.TextValue("3"))},
+ driver.NamedValue{Value: params.Named("$p0", types.Int32Value(1))},
+ driver.NamedValue{Value: params.Named("$p1", types.Uint64Value(2))},
+ driver.NamedValue{Value: params.Named("$p2", types.TextValue("3"))},
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
@@ -481,37 +482,37 @@ func TestArgsToParams(t *testing.T) {
driver.NamedValue{Value: uint64(2)},
driver.NamedValue{Value: "3"},
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
{
args: []interface{}{
driver.NamedValue{Value: table.NewQueryParameters(
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
)},
},
- params: []table.ParameterOption{
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params: []*params.Parameter{
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
},
err: nil,
},
{
args: []interface{}{
driver.NamedValue{Value: table.NewQueryParameters(
- table.ValueParam("$p0", types.Int32Value(1)),
- table.ValueParam("$p1", types.Uint64Value(2)),
- table.ValueParam("$p2", types.TextValue("3")),
+ params.Named("$p0", types.Int32Value(1)),
+ params.Named("$p1", types.Uint64Value(2)),
+ params.Named("$p2", types.TextValue("3")),
)},
- driver.NamedValue{Value: table.ValueParam("$p1", types.Uint64Value(2))},
- driver.NamedValue{Value: table.ValueParam("$p2", types.TextValue("3"))},
+ driver.NamedValue{Value: params.Named("$p1", types.Uint64Value(2))},
+ driver.NamedValue{Value: params.Named("$p2", types.TextValue("3"))},
},
err: errMultipleQueryParameters,
},
diff --git a/internal/cmd/gstack/main.go b/internal/cmd/gstack/main.go
new file mode 100644
index 000000000..10f3ed061
--- /dev/null
+++ b/internal/cmd/gstack/main.go
@@ -0,0 +1,228 @@
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/fs"
+ "os"
+ "path/filepath"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/cmd/gstack/utils"
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: gstack [path]\n")
+ flag.PrintDefaults()
+}
+
+func getCallExpressionsFromExpr(expr ast.Expr) (listOfCalls []*ast.CallExpr) {
+ switch expr := expr.(type) {
+ case *ast.SelectorExpr:
+ listOfCalls = getCallExpressionsFromExpr(expr.X)
+ case *ast.IndexExpr:
+ listOfCalls = getCallExpressionsFromExpr(expr.X)
+ case *ast.StarExpr:
+ listOfCalls = getCallExpressionsFromExpr(expr.X)
+ case *ast.BinaryExpr:
+ listOfCalls = getCallExpressionsFromExpr(expr.X)
+ listOfCalls = append(listOfCalls, getCallExpressionsFromExpr(expr.Y)...)
+ case *ast.CallExpr:
+ listOfCalls = append(listOfCalls, expr)
+ listOfCalls = append(listOfCalls, getCallExpressionsFromExpr(expr.Fun)...)
+ for _, arg := range expr.Args {
+ listOfCalls = append(listOfCalls, getCallExpressionsFromExpr(arg)...)
+ }
+ case *ast.CompositeLit:
+ for _, elt := range expr.Elts {
+ listOfCalls = append(listOfCalls, getCallExpressionsFromExpr(elt)...)
+ }
+ case *ast.UnaryExpr:
+ listOfCalls = append(listOfCalls, getCallExpressionsFromExpr(expr.X)...)
+ case *ast.KeyValueExpr:
+ listOfCalls = append(listOfCalls, getCallExpressionsFromExpr(expr.Value)...)
+ case *ast.FuncLit:
+ listOfCalls = append(listOfCalls, getListOfCallExpressionsFromBlockStmt(expr.Body)...)
+ }
+
+ return listOfCalls
+}
+
+func getExprFromDeclStmt(statement *ast.DeclStmt) (listOfExpressions []ast.Expr) {
+ decl, ok := statement.Decl.(*ast.GenDecl)
+ if !ok {
+ return listOfExpressions
+ }
+ for _, spec := range decl.Specs {
+ if spec, ok := spec.(*ast.ValueSpec); ok {
+ listOfExpressions = append(listOfExpressions, spec.Values...)
+ }
+ }
+
+ return listOfExpressions
+}
+
+func getCallExpressionsFromStmt(statement ast.Stmt) (listOfCallExpressions []*ast.CallExpr) {
+ var body *ast.BlockStmt
+ var listOfExpressions []ast.Expr
+ switch stmt := statement.(type) {
+ case *ast.IfStmt:
+ body = stmt.Body
+ case *ast.SwitchStmt:
+ body = stmt.Body
+ case *ast.TypeSwitchStmt:
+ body = stmt.Body
+ case *ast.SelectStmt:
+ body = stmt.Body
+ case *ast.ForStmt:
+ body = stmt.Body
+ case *ast.GoStmt:
+ if fun, ok := stmt.Call.Fun.(*ast.FuncLit); ok {
+ listOfCallExpressions = append(listOfCallExpressions, getListOfCallExpressionsFromBlockStmt(fun.Body)...)
+ } else {
+ listOfCallExpressions = append(listOfCallExpressions, stmt.Call)
+ }
+ case *ast.RangeStmt:
+ body = stmt.Body
+ case *ast.DeclStmt:
+ listOfExpressions = append(listOfExpressions, getExprFromDeclStmt(stmt)...)
+ for _, expr := range listOfExpressions {
+ listOfCallExpressions = append(listOfCallExpressions, getCallExpressionsFromExpr(expr)...)
+ }
+ case *ast.CommClause:
+ stmts := stmt.Body
+ for _, stmt := range stmts {
+ listOfCallExpressions = append(listOfCallExpressions, getCallExpressionsFromStmt(stmt)...)
+ }
+ case *ast.ExprStmt:
+ listOfCallExpressions = append(listOfCallExpressions, getCallExpressionsFromExpr(stmt.X)...)
+ case *ast.AssignStmt:
+ for _, rh := range stmt.Rhs {
+ listOfCallExpressions = append(listOfCallExpressions, getCallExpressionsFromExpr(rh)...)
+ }
+ case *ast.ReturnStmt:
+ for _, result := range stmt.Results {
+ listOfCallExpressions = append(listOfCallExpressions, getCallExpressionsFromExpr(result)...)
+ }
+ }
+ if body != nil {
+ listOfCallExpressions = append(
+ listOfCallExpressions,
+ getListOfCallExpressionsFromBlockStmt(body)...,
+ )
+ }
+
+ return listOfCallExpressions
+}
+
+func getListOfCallExpressionsFromBlockStmt(block *ast.BlockStmt) (listOfCallExpressions []*ast.CallExpr) {
+ for _, statement := range block.List {
+ listOfCallExpressions = append(listOfCallExpressions, getCallExpressionsFromStmt(statement)...)
+ }
+
+ return listOfCallExpressions
+}
+
+func format(src []byte, path string, fset *token.FileSet, file *ast.File) ([]byte, error) {
+ var listOfArgs []utils.FunctionIDArg
+ for _, f := range file.Decls {
+ var listOfCalls []*ast.CallExpr
+ fn, ok := f.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ listOfCalls = getListOfCallExpressionsFromBlockStmt(fn.Body)
+ for _, call := range listOfCalls {
+ if function, ok := call.Fun.(*ast.SelectorExpr); ok && function.Sel.Name == "FunctionID" {
+ pack, ok := function.X.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ if pack.Name == "stack" && len(call.Args) == 1 {
+ listOfArgs = append(listOfArgs, utils.FunctionIDArg{
+ FuncDecl: fn,
+ ArgPos: call.Args[0].Pos(),
+ ArgEnd: call.Args[0].End(),
+ })
+ }
+ }
+ }
+ }
+ if len(listOfArgs) != 0 {
+ fixed, err := utils.FixSource(fset, path, src, listOfArgs)
+ if err != nil {
+ return nil, err
+ }
+
+ return fixed, nil
+ }
+
+ return src, nil
+}
+
+func processFile(src []byte, path string, fset *token.FileSet, file *ast.File, info os.FileInfo) error {
+ formatted, err := format(src, path, fset, file)
+ if err != nil {
+ return err
+ }
+ if !bytes.Equal(src, formatted) {
+ err = utils.WriteFile(path, formatted, info.Mode().Perm())
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+ args := flag.Args()
+
+ if len(args) != 1 {
+ flag.Usage()
+
+ return
+ }
+ _, err := os.Stat(args[0])
+ if err != nil {
+ panic(err)
+ }
+
+ fileSystem := os.DirFS(args[0])
+
+ err = fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
+ fset := token.NewFileSet()
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+ if filepath.Ext(path) == ".go" {
+ info, err := os.Stat(path)
+ if err != nil {
+ return err
+ }
+ src, err := utils.ReadFile(path, info)
+ if err != nil {
+ return err
+ }
+ file, err := parser.ParseFile(fset, path, nil, 0)
+ if err != nil {
+ return err
+ }
+
+ return processFile(src, path, fset, file, info)
+ }
+
+ return nil
+ })
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/cmd/gstack/utils/utils.go b/internal/cmd/gstack/utils/utils.go
new file mode 100644
index 000000000..19b35512d
--- /dev/null
+++ b/internal/cmd/gstack/utils/utils.go
@@ -0,0 +1,135 @@
+package utils
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
+)
+
+type FunctionIDArg struct {
+ FuncDecl *ast.FuncDecl
+ ArgPos token.Pos
+ ArgEnd token.Pos
+}
+
+func ReadFile(filename string, info fs.FileInfo) ([]byte, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ size := int(info.Size())
+ src := make([]byte, size)
+ n, err := io.ReadFull(f, src)
+ if err != nil {
+ return nil, err
+ }
+ if n < size {
+ return nil, fmt.Errorf("error: size of %q changed during reading (from %d to %d bytes)", filename, size, n)
+ } else if n > size {
+ return nil, fmt.Errorf("error: size of %q changed during reading (from %d to >=%d bytes)", filename, size, len(src))
+ }
+
+ return src, nil
+}
+
+func FixSource(fset *token.FileSet, path string, src []byte, listOfArgs []FunctionIDArg) ([]byte, error) {
+ var fixed []byte
+ var previousArgEnd int
+ for _, arg := range listOfArgs {
+ argPosOffset := fset.Position(arg.ArgPos).Offset
+ argEndOffset := fset.Position(arg.ArgEnd).Offset
+ argument, err := makeCall(fset, path, arg)
+ if err != nil {
+ return nil, err
+ }
+ fixed = append(fixed, src[previousArgEnd:argPosOffset]...)
+ fixed = append(fixed, fmt.Sprintf("%q", argument)...)
+ previousArgEnd = argEndOffset
+ }
+ fixed = append(fixed, src[previousArgEnd:]...)
+
+ return fixed, nil
+}
+
+func WriteFile(filename string, formatted []byte, perm fs.FileMode) error {
+ fout, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, perm)
+ if err != nil {
+ return err
+ }
+
+ defer fout.Close()
+
+ _, err = fout.Write(formatted)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func makeCall(fset *token.FileSet, path string, arg FunctionIDArg) (string, error) {
+ basePath := filepath.Join("github.com", "ydb-platform", version.Prefix, version.Major, "")
+ packageName, err := getPackageName(fset, arg)
+ if err != nil {
+ return "", err
+ }
+ filePath := filepath.Dir(filepath.Dir(path))
+ funcName, err := getFuncName(arg.FuncDecl)
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Join(basePath, filePath, packageName) + "." + funcName, nil
+}
+
+func getFuncName(funcDecl *ast.FuncDecl) (string, error) {
+ if funcDecl.Recv != nil {
+ recvType := funcDecl.Recv.List[0].Type
+ prefix, err := getIdentNameFromExpr(recvType)
+ if err != nil {
+ return "", err
+ }
+
+ return prefix + "." + funcDecl.Name.Name, nil
+ }
+
+ return funcDecl.Name.Name, nil
+}
+
+func getIdentNameFromExpr(expr ast.Expr) (string, error) {
+ switch expr := expr.(type) {
+ case *ast.Ident:
+ return expr.Name, nil
+ case *ast.StarExpr:
+ prefix, err := getIdentNameFromExpr(expr.X)
+ if err != nil {
+ return "", err
+ }
+
+ return "(*" + prefix + ")", nil
+ case *ast.IndexExpr:
+ return getIdentNameFromExpr(expr.X)
+ case *ast.IndexListExpr:
+ return getIdentNameFromExpr(expr.X)
+ default:
+ return "", fmt.Errorf("error during getting ident from expr")
+ }
+}
+
+func getPackageName(fset *token.FileSet, arg FunctionIDArg) (string, error) {
+ file := fset.File(arg.ArgPos)
+ parsedFile, err := parser.ParseFile(fset, file.Name(), nil, parser.PackageClauseOnly)
+ if err != nil {
+ return "", fmt.Errorf("error during get package name function")
+ }
+
+ return parsedFile.Name.Name, nil
+}
diff --git a/internal/cmd/gtrace/.gitignore b/internal/cmd/gtrace/.gitignore
new file mode 100644
index 000000000..5239aed1b
--- /dev/null
+++ b/internal/cmd/gtrace/.gitignore
@@ -0,0 +1,2 @@
+gtrace
+gtrace.exe
diff --git a/internal/cmd/gtrace/gtrace b/internal/cmd/gtrace/gtrace
deleted file mode 100755
index 634cde9a3..000000000
Binary files a/internal/cmd/gtrace/gtrace and /dev/null differ
diff --git a/internal/cmd/gtrace/main.go b/internal/cmd/gtrace/main.go
index 685fba4df..30bc5c67d 100644
--- a/internal/cmd/gtrace/main.go
+++ b/internal/cmd/gtrace/main.go
@@ -16,7 +16,6 @@ import (
"os"
"path/filepath"
"strings"
- _ "unsafe" // For go:linkname.
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
)
@@ -70,8 +69,6 @@ func main() {
var writers []*Writer
if isGoGenerate {
- // We should respect Go suffixes like `_linux.go`.
- name, tags, ext := splitOSArchTags(&buildCtx, gofile)
openFile := func(name string) (*os.File, func()) {
var f *os.File
//nolint:gofumpt
@@ -88,7 +85,9 @@ func main() {
return f, func() { f.Close() }
}
- f, clean := openFile(name + "_gtrace" + tags + ext)
+ ext := filepath.Ext(gofile)
+ name := strings.TrimSuffix(gofile, ext)
+ f, clean := openFile(name + "_gtrace" + ext)
defer clean()
writers = append(writers, &Writer{
Context: buildCtx,
@@ -109,7 +108,7 @@ func main() {
)
fset := token.NewFileSet()
for _, name := range buildPkg.GoFiles {
- base, _, _ := splitOSArchTags(&buildCtx, name)
+ base := strings.TrimSuffix(name, filepath.Ext(name))
if isGenerated(base, "_gtrace") {
continue
}
diff --git a/internal/conn/config.go b/internal/conn/config.go
index 305fc8c71..df82f3a85 100644
--- a/internal/conn/config.go
+++ b/internal/conn/config.go
@@ -10,7 +10,7 @@ import (
type Config interface {
DialTimeout() time.Duration
- Trace() *trace.Driver
ConnectionTTL() time.Duration
+ Trace() *trace.Driver
GrpcDialOptions() []grpc.DialOption
}
diff --git a/internal/conn/conn.go b/internal/conn/conn.go
index cec97c202..66278bb94 100644
--- a/internal/conn/conn.go
+++ b/internal/conn/conn.go
@@ -55,7 +55,7 @@ type conn struct {
endpoint endpoint.Endpoint // ro access
closed bool
state atomic.Uint32
- lastUsage time.Time
+ lastUsage *lastUsage
onClose []func(*conn)
onTransportErrors []func(ctx context.Context, cc Conn, cause error)
}
@@ -80,7 +80,7 @@ func (c *conn) LastUsage() time.Time {
c.mtx.RLock()
defer c.mtx.RUnlock()
- return c.lastUsage
+ return c.lastUsage.Get()
}
func (c *conn) IsState(states ...State) bool {
@@ -94,10 +94,18 @@ func (c *conn) IsState(states ...State) bool {
return false
}
+func (c *conn) NodeID() uint32 {
+ if c != nil {
+ return c.endpoint.NodeID()
+ }
+
+ return 0
+}
+
func (c *conn) park(ctx context.Context) (err error) {
onDone := trace.DriverOnConnPark(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*conn).park"),
c.Endpoint(),
)
defer func() {
@@ -124,14 +132,6 @@ func (c *conn) park(ctx context.Context) (err error) {
return nil
}
-func (c *conn) NodeID() uint32 {
- if c != nil {
- return c.endpoint.NodeID()
- }
-
- return 0
-}
-
func (c *conn) Endpoint() endpoint.Endpoint {
if c != nil {
return c.endpoint
@@ -148,7 +148,7 @@ func (c *conn) setState(ctx context.Context, s State) State {
if state := State(c.state.Swap(uint32(s))); state != s {
trace.DriverOnConnStateChange(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*conn).setState"),
c.endpoint.Copy(), state,
)(s)
}
@@ -196,7 +196,7 @@ func (c *conn) realConn(ctx context.Context) (cc *grpc.ClientConn, err error) {
onDone := trace.DriverOnConnDial(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*conn).realConn"),
c.endpoint.Copy(),
)
defer func() {
@@ -213,6 +213,10 @@ func (c *conn) realConn(ctx context.Context) (cc *grpc.ClientConn, err error) {
}, c.config.GrpcDialOptions()...,
)...)
if err != nil {
+ if xerrors.IsContextError(err) {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
defer func() {
c.onTransportError(ctx, err)
}()
@@ -240,12 +244,6 @@ func (c *conn) onTransportError(ctx context.Context, cause error) {
}
}
-func (c *conn) touchLastUsage() {
- c.mtx.Lock()
- defer c.mtx.Unlock()
- c.lastUsage = time.Now()
-}
-
func isAvailable(raw *grpc.ClientConn) bool {
return raw != nil && raw.GetState() == connectivity.Ready
}
@@ -279,7 +277,7 @@ func (c *conn) Close(ctx context.Context) (err error) {
onDone := trace.DriverOnConnClose(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*conn).Close"),
c.Endpoint(),
)
defer func() {
@@ -312,7 +310,7 @@ func (c *conn) Invoke(
useWrapping = UseWrapping(ctx)
onDone = trace.DriverOnConnInvoke(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*conn).Invoke"),
c.endpoint, trace.Method(method),
)
cc *grpc.ClientConn
@@ -328,8 +326,8 @@ func (c *conn) Invoke(
return c.wrapError(err)
}
- c.touchLastUsage()
- defer c.touchLastUsage()
+ stop := c.lastUsage.Start()
+ defer stop()
ctx, traceID, err := meta.TraceID(ctx)
if err != nil {
@@ -340,6 +338,10 @@ func (c *conn) Invoke(
err = cc.Invoke(ctx, method, req, res, append(opts, grpc.Trailer(&md))...)
if err != nil {
+ if xerrors.IsContextError(err) {
+ return xerrors.WithStackTrace(err)
+ }
+
defer func() {
c.onTransportError(ctx, err)
}()
@@ -384,6 +386,7 @@ func (c *conn) Invoke(
return err
}
+//nolint:funlen
func (c *conn) NewStream(
ctx context.Context,
desc *grpc.StreamDesc,
@@ -391,9 +394,9 @@ func (c *conn) NewStream(
opts ...grpc.CallOption,
) (_ grpc.ClientStream, err error) {
var (
- streamRecv = trace.DriverOnConnNewStream(
+ onDone = trace.DriverOnConnNewStream(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*conn).NewStream"),
c.endpoint.Copy(), trace.Method(method),
)
useWrapping = UseWrapping(ctx)
@@ -402,18 +405,7 @@ func (c *conn) NewStream(
)
defer func() {
- if err != nil {
- streamRecv(err)(err, c.GetState(), metadata.MD{})
- }
- }()
-
- var cancel context.CancelFunc
- ctx, cancel = xcontext.WithCancel(ctx)
-
- defer func() {
- if err != nil {
- cancel()
- }
+ onDone(err, c.GetState())
}()
cc, err = c.realConn(ctx)
@@ -421,8 +413,8 @@ func (c *conn) NewStream(
return nil, c.wrapError(err)
}
- c.touchLastUsage()
- defer c.touchLastUsage()
+ stop := c.lastUsage.Start()
+ defer stop()
ctx, traceID, err := meta.TraceID(ctx)
if err != nil {
@@ -433,6 +425,10 @@ func (c *conn) NewStream(
s, err = cc.NewStream(ctx, desc, method, opts...)
if err != nil {
+ if xerrors.IsContextError(err) {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
defer func() {
c.onTransportError(ctx, err)
}()
@@ -454,15 +450,14 @@ func (c *conn) NewStream(
return &grpcClientStream{
ClientStream: s,
+ ctx: ctx,
c: c,
wrapping: useWrapping,
traceID: traceID,
sentMark: sentMark,
onDone: func(ctx context.Context, md metadata.MD) {
- cancel()
meta.CallTrailerCallback(ctx, md)
},
- recv: streamRecv,
}, nil
}
@@ -495,14 +490,15 @@ func withOnTransportError(onTransportError func(ctx context.Context, cc Conn, ca
func newConn(e endpoint.Endpoint, config Config, opts ...option) *conn {
c := &conn{
- endpoint: e,
- config: config,
- done: make(chan struct{}),
+ endpoint: e,
+ config: config,
+ done: make(chan struct{}),
+ lastUsage: newLastUsage(nil),
}
c.state.Store(uint32(Created))
- for _, o := range opts {
- if o != nil {
- o(c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
}
}
diff --git a/internal/conn/error_test.go b/internal/conn/error_test.go
index cd9e68b71..569a38a31 100644
--- a/internal/conn/error_test.go
+++ b/internal/conn/error_test.go
@@ -32,30 +32,30 @@ func TestNodeErrorIs(t *testing.T) {
require.NotErrorIs(t, nodeErr, testErr2)
}
-type testErrorType1 struct {
+type testType1Error struct {
msg string
}
-func (t testErrorType1) Error() string {
+func (t testType1Error) Error() string {
return "1 - " + t.msg
}
-type testErrorType2 struct {
+type testType2Error struct {
msg string
}
-func (t testErrorType2) Error() string {
+func (t testType2Error) Error() string {
return "2 - " + t.msg
}
func TestNodeErrorAs(t *testing.T) {
- testErr := testErrorType1{msg: "test"}
+ testErr := testType1Error{msg: "test"}
nodeErr := newConnError(1, "localhost:1234", testErr)
- target := testErrorType1{}
+ target := testType1Error{}
require.ErrorAs(t, nodeErr, &target)
require.Equal(t, testErr, target)
- target2 := testErrorType2{}
+ target2 := testType2Error{}
require.False(t, errors.As(nodeErr, &target2))
}
diff --git a/internal/conn/grpc_client_stream.go b/internal/conn/grpc_client_stream.go
index ca07acb79..32377e5ab 100644
--- a/internal/conn/grpc_client_stream.go
+++ b/internal/conn/grpc_client_stream.go
@@ -3,32 +3,45 @@ package conn
import (
"context"
"io"
- "time"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/wrap"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
type grpcClientStream struct {
grpc.ClientStream
+ ctx context.Context
c *conn
wrapping bool
traceID string
sentMark *modificationMark
onDone func(ctx context.Context, md metadata.MD)
- recv func(error) func(error, trace.ConnState, map[string][]string)
}
func (s *grpcClientStream) CloseSend() (err error) {
+ onDone := trace.DriverOnConnStreamCloseSend(s.c.config.Trace(), &s.ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*grpcClientStream).CloseSend"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ stop := s.c.lastUsage.Start()
+ defer stop()
+
err = s.ClientStream.CloseSend()
if err != nil {
+ if xerrors.IsContextError(err) {
+ return xerrors.WithStackTrace(err)
+ }
+
if s.wrapping {
return s.wrapError(
xerrors.Transport(
@@ -46,12 +59,23 @@ func (s *grpcClientStream) CloseSend() (err error) {
}
func (s *grpcClientStream) SendMsg(m interface{}) (err error) {
- cancel := createPinger(s.c)
- defer cancel()
+ onDone := trace.DriverOnConnStreamSendMsg(s.c.config.Trace(), &s.ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*grpcClientStream).SendMsg"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ stop := s.c.lastUsage.Start()
+ defer stop()
err = s.ClientStream.SendMsg(m)
if err != nil {
+ if xerrors.IsContextError(err) {
+ return xerrors.WithStackTrace(err)
+ }
+
defer func() {
s.c.onTransportError(s.Context(), err)
}()
@@ -77,21 +101,30 @@ func (s *grpcClientStream) SendMsg(m interface{}) (err error) {
}
func (s *grpcClientStream) RecvMsg(m interface{}) (err error) {
- cancel := createPinger(s.c)
- defer cancel()
+ onDone := trace.DriverOnConnStreamRecvMsg(s.c.config.Trace(), &s.ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*grpcClientStream).RecvMsg"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ stop := s.c.lastUsage.Start()
+ defer stop()
defer func() {
- onDone := s.recv(xerrors.HideEOF(err))
if err != nil {
md := s.ClientStream.Trailer()
- onDone(xerrors.HideEOF(err), s.c.GetState(), md)
- s.onDone(s.ClientStream.Context(), md)
+ s.onDone(s.ctx, md)
}
}()
err = s.ClientStream.RecvMsg(m)
- if err != nil {
+ if err != nil { //nolint:nestif
+ if xerrors.IsContextError(err) {
+ return xerrors.WithStackTrace(err)
+ }
+
defer func() {
if !xerrors.Is(err, io.EOF) {
s.c.onTransportError(s.Context(), err)
@@ -135,28 +168,8 @@ func (s *grpcClientStream) wrapError(err error) error {
return nil
}
- nodeErr := newConnError(s.c.endpoint.NodeID(), s.c.endpoint.Address(), err)
-
- return xerrors.WithStackTrace(nodeErr, xerrors.WithSkipDepth(1))
-}
-
-func createPinger(c *conn) context.CancelFunc {
- c.touchLastUsage()
- ctx, cancel := xcontext.WithCancel(context.Background())
- go func() {
- ticker := time.NewTicker(time.Second)
- ctxDone := ctx.Done()
- for {
- select {
- case <-ctxDone:
- ticker.Stop()
-
- return
- case <-ticker.C:
- c.touchLastUsage()
- }
- }
- }()
-
- return cancel
+ return xerrors.WithStackTrace(
+ newConnError(s.c.endpoint.NodeID(), s.c.endpoint.Address(), err),
+ xerrors.WithSkipDepth(1),
+ )
}
diff --git a/internal/conn/last_usage.go b/internal/conn/last_usage.go
new file mode 100644
index 000000000..b0ca293a9
--- /dev/null
+++ b/internal/conn/last_usage.go
@@ -0,0 +1,47 @@
+package conn
+
+import (
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/jonboulle/clockwork"
+)
+
+type lastUsage struct {
+ locks atomic.Int64
+ t atomic.Pointer[time.Time]
+ clock clockwork.Clock
+}
+
+func newLastUsage(clock clockwork.Clock) *lastUsage {
+ if clock == nil {
+ clock = clockwork.NewRealClock()
+ }
+ now := clock.Now()
+ usage := &lastUsage{
+ clock: clock,
+ }
+ usage.t.Store(&now)
+
+ return usage
+}
+
+func (l *lastUsage) Get() time.Time {
+ if l.locks.Load() == 0 {
+ return *l.t.Load()
+ }
+
+ return l.clock.Now()
+}
+
+func (l *lastUsage) Start() (stop func()) {
+ l.locks.Add(1)
+
+ return sync.OnceFunc(func() {
+ if l.locks.Add(-1) == 0 {
+ now := l.clock.Now()
+ l.t.Store(&now)
+ }
+ })
+}
diff --git a/internal/conn/last_usage_test.go b/internal/conn/last_usage_test.go
new file mode 100644
index 000000000..b7c79695e
--- /dev/null
+++ b/internal/conn/last_usage_test.go
@@ -0,0 +1,98 @@
+package conn
+
+import (
+ "testing"
+ "time"
+
+ "github.com/jonboulle/clockwork"
+ "github.com/stretchr/testify/require"
+)
+
+func Test_lastUsage_Lock(t *testing.T) {
+ t.Run("NowFromLocked", func(t *testing.T) {
+ start := time.Unix(0, 0)
+ clock := clockwork.NewFakeClockAt(start)
+ lu := &lastUsage{
+ clock: clock,
+ }
+ lu.t.Store(&start)
+ t1 := lu.Get()
+ require.Equal(t, start, t1)
+ f := lu.Start()
+ clock.Advance(time.Hour)
+ t2 := lu.Get()
+ require.Equal(t, start.Add(time.Hour), t2)
+ clock.Advance(time.Hour)
+ f()
+ t3 := lu.Get()
+ require.Equal(t, start.Add(2*time.Hour), t3)
+ clock.Advance(time.Hour)
+ t4 := lu.Get()
+ require.Equal(t, start.Add(2*time.Hour), t4)
+ })
+ t.Run("UpdateAfterLastUnlock", func(t *testing.T) {
+ start := time.Unix(0, 0)
+ clock := clockwork.NewFakeClockAt(start)
+ lu := &lastUsage{
+ clock: clock,
+ }
+ lu.t.Store(&start)
+ t1 := lu.Get()
+ require.Equal(t, start, t1)
+ f1 := lu.Start()
+ clock.Advance(time.Hour)
+ t2 := lu.Get()
+ require.Equal(t, start.Add(time.Hour), t2)
+ f2 := lu.Start()
+ clock.Advance(time.Hour)
+ f1()
+ f3 := lu.Start()
+ clock.Advance(time.Hour)
+ t3 := lu.Get()
+ require.Equal(t, start.Add(3*time.Hour), t3)
+ clock.Advance(time.Hour)
+ t4 := lu.Get()
+ require.Equal(t, start.Add(4*time.Hour), t4)
+ f3()
+ t5 := lu.Get()
+ require.Equal(t, start.Add(4*time.Hour), t5)
+ clock.Advance(time.Hour)
+ t6 := lu.Get()
+ require.Equal(t, start.Add(5*time.Hour), t6)
+ clock.Advance(time.Hour)
+ f2()
+ t7 := lu.Get()
+ require.Equal(t, start.Add(6*time.Hour), t7)
+ clock.Advance(time.Hour)
+ f2()
+ t8 := lu.Get()
+ require.Equal(t, start.Add(6*time.Hour), t8)
+ })
+ t.Run("DeferRelease", func(t *testing.T) {
+ start := time.Unix(0, 0)
+ clock := clockwork.NewFakeClockAt(start)
+ lu := &lastUsage{
+ clock: clock,
+ }
+ lu.t.Store(&start)
+
+ func() {
+ t1 := lu.Get()
+ require.Equal(t, start, t1)
+ clock.Advance(time.Hour)
+ t2 := lu.Get()
+ require.Equal(t, start, t2)
+ clock.Advance(time.Hour)
+ defer lu.Start()()
+ t3 := lu.Get()
+ require.Equal(t, start.Add(2*time.Hour), t3)
+ clock.Advance(time.Hour)
+ t4 := lu.Get()
+ require.Equal(t, start.Add(3*time.Hour), t4)
+ clock.Advance(time.Hour)
+ }()
+ clock.Advance(time.Hour)
+ t5 := lu.Get()
+ require.Equal(t, start.Add(4*time.Hour), t5)
+ })
+}
diff --git a/internal/conn/pool.go b/internal/conn/pool.go
index 48a9713a0..246627333 100644
--- a/internal/conn/pool.go
+++ b/internal/conn/pool.go
@@ -7,6 +7,7 @@ import (
"time"
"google.golang.org/grpc"
+ grpcCodes "google.golang.org/grpc/codes"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/closer"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/endpoint"
@@ -79,6 +80,28 @@ func (p *Pool) Ban(ctx context.Context, cc Conn, cause error) {
return
}
+ if !xerrors.IsTransportError(cause,
+ grpcCodes.ResourceExhausted,
+ grpcCodes.Unavailable,
+ // grpcCodes.OK,
+ // grpcCodes.Canceled,
+ // grpcCodes.Unknown,
+ // grpcCodes.InvalidArgument,
+ // grpcCodes.DeadlineExceeded,
+ // grpcCodes.NotFound,
+ // grpcCodes.AlreadyExists,
+ // grpcCodes.PermissionDenied,
+ // grpcCodes.FailedPrecondition,
+ // grpcCodes.Aborted,
+ // grpcCodes.OutOfRange,
+ // grpcCodes.Unimplemented,
+ // grpcCodes.Internal,
+ // grpcCodes.DataLoss,
+ // grpcCodes.Unauthenticated,
+ ) {
+ return
+ }
+
e := cc.Endpoint().Copy()
p.mtx.RLock()
@@ -91,7 +114,7 @@ func (p *Pool) Ban(ctx context.Context, cc Conn, cause error) {
trace.DriverOnConnBan(
p.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*Pool).Ban"),
e, cc.GetState(), cause,
)(cc.SetState(ctx, Banned))
}
@@ -113,7 +136,7 @@ func (p *Pool) Allow(ctx context.Context, cc Conn) {
trace.DriverOnConnAllow(
p.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*Pool).Allow"),
e, cc.GetState(),
)(cc.Unban(ctx))
}
@@ -125,7 +148,9 @@ func (p *Pool) Take(context.Context) error {
}
func (p *Pool) Release(ctx context.Context) (finalErr error) {
- onDone := trace.DriverOnPoolRelease(p.config.Trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.DriverOnPoolRelease(p.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.(*Pool).Release"),
+ )
defer func() {
onDone(finalErr)
}()
@@ -207,7 +232,9 @@ func (p *Pool) collectConns() []*conn {
}
func NewPool(ctx context.Context, config Config) *Pool {
- onDone := trace.DriverOnPoolNew(config.Trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.DriverOnPoolNew(config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/conn.NewPool"),
+ )
defer onDone()
p := &Pool{
@@ -217,6 +244,7 @@ func NewPool(ctx context.Context, config Config) *Pool {
conns: make(map[connsKey]*conn),
done: make(chan struct{}),
}
+
if ttl := config.ConnectionTTL(); ttl > 0 {
go p.connParker(xcontext.WithoutDeadline(ctx), ttl, ttl/2) //nolint:gomnd
}
diff --git a/internal/coordination/client.go b/internal/coordination/client.go
index c6ceede63..70322f702 100644
--- a/internal/coordination/client.go
+++ b/internal/coordination/client.go
@@ -3,157 +3,216 @@ package coordination
import (
"context"
"errors"
+ "sync"
+ "time"
"github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations"
"google.golang.org/grpc"
"github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
"github.com/ydb-platform/ydb-go-sdk/v3/scheme"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
-//nolint:gofumpt
-//nolint:nolintlint
-var (
- errNilClient = xerrors.Wrap(errors.New("coordination client is not initialized"))
-)
+//go:generate mockgen -destination grpc_client_mock_test.go -package coordination -write_package_comment=false github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1 CoordinationServiceClient,CoordinationService_SessionClient
+
+var errNilClient = xerrors.Wrap(errors.New("coordination client is not initialized"))
type Client struct {
- config config.Config
- service Ydb_Coordination_V1.CoordinationServiceClient
+ config config.Config
+ client Ydb_Coordination_V1.CoordinationServiceClient
+
+ mutex sync.Mutex // guards the fields below
+ sessions map[*session]struct{}
}
-func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) (*Client, error) {
+func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client {
+ onDone := trace.CoordinationOnNew(config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.New"),
+ )
+ defer onDone()
+
return &Client{
- config: config,
- service: Ydb_Coordination_V1.NewCoordinationServiceClient(cc),
- }, nil
+ config: config,
+ client: Ydb_Coordination_V1.NewCoordinationServiceClient(cc),
+ sessions: make(map[*session]struct{}),
+ }
}
-func (c *Client) CreateNode(ctx context.Context, path string, config coordination.NodeConfig) error {
- if c == nil {
- return xerrors.WithStackTrace(errNilClient)
- }
- call := func(ctx context.Context) error {
- return xerrors.WithStackTrace(c.createNode(ctx, path, config))
+func operationParams(
+ ctx context.Context,
+ config interface {
+ OperationTimeout() time.Duration
+ OperationCancelAfter() time.Duration
+ },
+ mode operation.Mode,
+) *Ydb_Operations.OperationParams {
+ return operation.Params(
+ ctx,
+ config.OperationTimeout(),
+ config.OperationCancelAfter(),
+ mode,
+ )
+}
+
+func createNodeRequest(
+ path string, config coordination.NodeConfig, operationParams *Ydb_Operations.OperationParams,
+) *Ydb_Coordination.CreateNodeRequest {
+ return &Ydb_Coordination.CreateNodeRequest{
+ Path: path,
+ Config: &Ydb_Coordination.Config{
+ Path: config.Path,
+ SelfCheckPeriodMillis: config.SelfCheckPeriodMillis,
+ SessionGracePeriodMillis: config.SessionGracePeriodMillis,
+ ReadConsistencyMode: config.ReadConsistencyMode.To(),
+ AttachConsistencyMode: config.AttachConsistencyMode.To(),
+ RateLimiterCountersMode: config.RatelimiterCountersMode.To(),
+ },
+ OperationParams: operationParams,
}
- if !c.config.AutoRetry() {
- return xerrors.WithStackTrace(call(ctx))
+}
+
+func createNode(
+ ctx context.Context, client Ydb_Coordination_V1.CoordinationServiceClient, request *Ydb_Coordination.CreateNodeRequest,
+) error {
+ _, err := client.CreateNode(ctx, request)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
}
- return retry.Retry(ctx,
- call, retry.WithStackTrace(),
- retry.WithIdempotent(true),
- retry.WithTrace(c.config.TraceRetry()),
- )
+ return nil
}
-func (c *Client) createNode(ctx context.Context, path string, config coordination.NodeConfig) error {
- _, err := c.service.CreateNode(
- ctx,
- &Ydb_Coordination.CreateNodeRequest{
- Path: path,
- Config: &Ydb_Coordination.Config{
- Path: config.Path,
- SelfCheckPeriodMillis: config.SelfCheckPeriodMillis,
- SessionGracePeriodMillis: config.SessionGracePeriodMillis,
- ReadConsistencyMode: config.ReadConsistencyMode.To(),
- AttachConsistencyMode: config.AttachConsistencyMode.To(),
- RateLimiterCountersMode: config.RatelimiterCountersMode.To(),
- },
- OperationParams: operation.Params(
- ctx,
- c.config.OperationTimeout(),
- c.config.OperationCancelAfter(),
- operation.ModeSync,
- ),
- },
+func (c *Client) CreateNode(ctx context.Context, path string, config coordination.NodeConfig) (finalErr error) {
+ if c == nil {
+ return xerrors.WithStackTrace(errNilClient)
+ }
+
+ onDone := trace.CoordinationOnCreateNode(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.(*Client).CreateNode"),
+ path,
)
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ request := createNodeRequest(path, config, operationParams(ctx, &c.config, operation.ModeSync))
+
+ if !c.config.AutoRetry() {
+ return createNode(ctx, c.client, request)
+ }
- return xerrors.WithStackTrace(err)
+ return retry.Retry(ctx, func(ctx context.Context) error {
+ return createNode(ctx, c.client, request)
+ }, retry.WithStackTrace(), retry.WithIdempotent(true), retry.WithTrace(c.config.TraceRetry()))
}
-func (c *Client) AlterNode(ctx context.Context, path string, config coordination.NodeConfig) error {
+func (c *Client) AlterNode(ctx context.Context, path string, config coordination.NodeConfig) (finalErr error) {
if c == nil {
return xerrors.WithStackTrace(errNilClient)
}
+
+ onDone := trace.CoordinationOnAlterNode(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.(*Client).AlterNode"),
+ path,
+ )
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ request := alterNodeRequest(path, config, operationParams(ctx, &c.config, operation.ModeSync))
+
call := func(ctx context.Context) error {
- return xerrors.WithStackTrace(c.alterNode(ctx, path, config))
+ return alterNode(ctx, c.client, request)
}
if !c.config.AutoRetry() {
return xerrors.WithStackTrace(call(ctx))
}
- return retry.Retry(ctx,
- call,
- retry.WithStackTrace(),
- retry.WithIdempotent(true),
- retry.WithTrace(c.config.TraceRetry()),
- )
+ return retry.Retry(ctx, func(ctx context.Context) (err error) {
+ return alterNode(ctx, c.client, request)
+ }, retry.WithStackTrace(), retry.WithIdempotent(true), retry.WithTrace(c.config.TraceRetry()))
}
-func (c *Client) alterNode(ctx context.Context, path string, config coordination.NodeConfig) error {
- _, err := c.service.AlterNode(
- ctx,
- &Ydb_Coordination.AlterNodeRequest{
- Path: path,
- Config: &Ydb_Coordination.Config{
- Path: config.Path,
- SelfCheckPeriodMillis: config.SelfCheckPeriodMillis,
- SessionGracePeriodMillis: config.SessionGracePeriodMillis,
- ReadConsistencyMode: config.ReadConsistencyMode.To(),
- AttachConsistencyMode: config.AttachConsistencyMode.To(),
- RateLimiterCountersMode: config.RatelimiterCountersMode.To(),
- },
- OperationParams: operation.Params(
- ctx,
- c.config.OperationTimeout(),
- c.config.OperationCancelAfter(),
- operation.ModeSync,
- ),
+func alterNodeRequest(
+ path string, config coordination.NodeConfig, operationParams *Ydb_Operations.OperationParams,
+) *Ydb_Coordination.AlterNodeRequest {
+ return &Ydb_Coordination.AlterNodeRequest{
+ Path: path,
+ Config: &Ydb_Coordination.Config{
+ Path: config.Path,
+ SelfCheckPeriodMillis: config.SelfCheckPeriodMillis,
+ SessionGracePeriodMillis: config.SessionGracePeriodMillis,
+ ReadConsistencyMode: config.ReadConsistencyMode.To(),
+ AttachConsistencyMode: config.AttachConsistencyMode.To(),
+ RateLimiterCountersMode: config.RatelimiterCountersMode.To(),
},
- )
+ OperationParams: operationParams,
+ }
+}
- return xerrors.WithStackTrace(err)
+func alterNode(
+ ctx context.Context, client Ydb_Coordination_V1.CoordinationServiceClient, request *Ydb_Coordination.AlterNodeRequest,
+) error {
+ _, err := client.AlterNode(ctx, request)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
}
-func (c *Client) DropNode(ctx context.Context, path string) error {
+func (c *Client) DropNode(ctx context.Context, path string) (finalErr error) {
if c == nil {
return xerrors.WithStackTrace(errNilClient)
}
+
+ onDone := trace.CoordinationOnDropNode(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.(*Client).DropNode"),
+ path,
+ )
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ request := dropNodeRequest(path, operationParams(ctx, &c.config, operation.ModeSync))
+
call := func(ctx context.Context) error {
- return xerrors.WithStackTrace(c.dropNode(ctx, path))
+ return dropNode(ctx, c.client, request)
}
if !c.config.AutoRetry() {
return xerrors.WithStackTrace(call(ctx))
}
- return retry.Retry(ctx, call,
- retry.WithStackTrace(),
- retry.WithIdempotent(true),
- retry.WithTrace(c.config.TraceRetry()),
- )
+ return retry.Retry(ctx, func(ctx context.Context) (err error) {
+ return dropNode(ctx, c.client, request)
+ }, retry.WithStackTrace(), retry.WithIdempotent(true), retry.WithTrace(c.config.TraceRetry()))
}
-func (c *Client) dropNode(ctx context.Context, path string) error {
- _, err := c.service.DropNode(
- ctx,
- &Ydb_Coordination.DropNodeRequest{
- Path: path,
- OperationParams: operation.Params(
- ctx,
- c.config.OperationTimeout(),
- c.config.OperationCancelAfter(),
- operation.ModeSync,
- ),
- },
- )
+func dropNodeRequest(path string, operationParams *Ydb_Operations.OperationParams) *Ydb_Coordination.DropNodeRequest {
+ return &Ydb_Coordination.DropNodeRequest{
+ Path: path,
+ OperationParams: operationParams,
+ }
+}
+
+func dropNode(
+ ctx context.Context, client Ydb_Coordination_V1.CoordinationServiceClient, request *Ydb_Coordination.DropNodeRequest,
+) error {
+ _, err := client.DropNode(ctx, request)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
- return xerrors.WithStackTrace(err)
+ return nil
}
func (c *Client) DescribeNode(
@@ -162,34 +221,55 @@ func (c *Client) DescribeNode(
) (
entry *scheme.Entry,
config *coordination.NodeConfig,
- _ error,
+ finalErr error,
) {
if c == nil {
return nil, nil, xerrors.WithStackTrace(errNilClient)
}
- call := func(ctx context.Context) (err error) {
- entry, config, err = c.describeNode(ctx, path)
- return xerrors.WithStackTrace(err)
- }
+ onDone := trace.CoordinationOnDescribeNode(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.(*Client).DescribeNode"),
+ path,
+ )
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ request := describeNodeRequest(path, operationParams(ctx, &c.config, operation.ModeSync))
+
if !c.config.AutoRetry() {
- err := call(ctx)
+ return describeNode(ctx, c.client, request)
+ }
- return entry, config, xerrors.WithStackTrace(err)
+ err := retry.Retry(ctx, func(ctx context.Context) (err error) {
+ entry, config, err = describeNode(ctx, c.client, request)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+ }, retry.WithStackTrace(), retry.WithIdempotent(true), retry.WithTrace(c.config.TraceRetry()))
+ if err != nil {
+ return nil, nil, xerrors.WithStackTrace(err)
}
- err := retry.Retry(ctx, call,
- retry.WithStackTrace(),
- retry.WithIdempotent(true),
- retry.WithTrace(c.config.TraceRetry()),
- )
- return entry, config, xerrors.WithStackTrace(err)
+ return entry, config, nil
+}
+
+func describeNodeRequest(
+ path string, operationParams *Ydb_Operations.OperationParams,
+) *Ydb_Coordination.DescribeNodeRequest {
+ return &Ydb_Coordination.DescribeNodeRequest{
+ Path: path,
+ OperationParams: operationParams,
+ }
}
// DescribeNode describes a coordination node
-func (c *Client) describeNode(
+func describeNode(
ctx context.Context,
- path string,
+ client Ydb_Coordination_V1.CoordinationServiceClient,
+ request *Ydb_Coordination.DescribeNodeRequest,
) (
_ *scheme.Entry,
_ *coordination.NodeConfig,
@@ -199,21 +279,11 @@ func (c *Client) describeNode(
response *Ydb_Coordination.DescribeNodeResponse
result Ydb_Coordination.DescribeNodeResult
)
- response, err = c.service.DescribeNode(
- ctx,
- &Ydb_Coordination.DescribeNodeRequest{
- Path: path,
- OperationParams: operation.Params(
- ctx,
- c.config.OperationTimeout(),
- c.config.OperationCancelAfter(),
- operation.ModeSync,
- ),
- },
- )
+ response, err = client.DescribeNode(ctx, request)
if err != nil {
return nil, nil, xerrors.WithStackTrace(err)
}
+
err = response.GetOperation().GetResult().UnmarshalTo(&result)
if err != nil {
return nil, nil, xerrors.WithStackTrace(err)
@@ -229,15 +299,85 @@ func (c *Client) describeNode(
}, nil
}
-func (c *Client) Close(ctx context.Context) error {
+func newCreateSessionConfig(opts ...options.SessionOption) *options.CreateSessionOptions {
+ c := defaultCreateSessionConfig()
+ for _, o := range opts {
+ if o != nil {
+ o(c)
+ }
+ }
+
+ return c
+}
+
+func (c *Client) sessionCreated(s *session) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ c.sessions[s] = struct{}{}
+}
+
+func (c *Client) sessionClosed(s *session) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ delete(c.sessions, s)
+}
+
+func (c *Client) closeSessions(ctx context.Context) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ for s := range c.sessions {
+ s.Close(ctx)
+ }
+}
+
+func defaultCreateSessionConfig() *options.CreateSessionOptions {
+ return &options.CreateSessionOptions{
+ Description: "YDB Go SDK",
+ SessionTimeout: time.Second * 5,
+ SessionStartTimeout: time.Second * 1,
+ SessionStopTimeout: time.Second * 1,
+ SessionKeepAliveTimeout: time.Second * 10,
+ SessionReconnectDelay: time.Millisecond * 500,
+ }
+}
+
+func (c *Client) Session(
+ ctx context.Context,
+ path string,
+ opts ...options.SessionOption,
+) (_ coordination.Session, finalErr error) {
if c == nil {
- return xerrors.WithStackTrace(errNilClient)
+ return nil, xerrors.WithStackTrace(errNilClient)
}
- return c.close(ctx)
+ onDone := trace.CoordinationOnSession(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.(*Client).Session"),
+ path,
+ )
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ return createSession(ctx, c, path, newCreateSessionConfig(opts...))
}
-func (c *Client) close(context.Context) error {
+func (c *Client) Close(ctx context.Context) (finalErr error) {
+ if c == nil {
+ return xerrors.WithStackTrace(errNilClient)
+ }
+
+ onDone := trace.CoordinationOnClose(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/coordination.(*Client).Close"),
+ )
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ c.closeSessions(ctx)
+
return nil
}
diff --git a/internal/coordination/client_test.go b/internal/coordination/client_test.go
new file mode 100644
index 000000000..d7778be95
--- /dev/null
+++ b/internal/coordination/client_test.go
@@ -0,0 +1,388 @@
+package coordination
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Scheme"
+ "go.uber.org/mock/gomock"
+ grpcCodes "google.golang.org/grpc/codes"
+ grpcStatus "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/types/known/anypb"
+ "google.golang.org/protobuf/types/known/durationpb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+ "github.com/ydb-platform/ydb-go-sdk/v3/scheme"
+)
+
+func TestCreateNode(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().CreateNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.CreateNodeResponse{
+ Operation: &Ydb_Operations.Operation{
+ Ready: true,
+ Status: Ydb.StatusIds_SUCCESS,
+ },
+ }, nil)
+ err := createNode(ctx, client, &Ydb_Coordination.CreateNodeRequest{})
+ require.NoError(t, err)
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().CreateNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Transport(grpcStatus.Error(grpcCodes.ResourceExhausted, "")),
+ )
+ err := createNode(ctx, client, &Ydb_Coordination.CreateNodeRequest{})
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.ResourceExhausted))
+ require.True(t, xerrors.IsRetryObjectValid(err))
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().CreateNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ err := createNode(ctx, client, &Ydb_Coordination.CreateNodeRequest{})
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ require.True(t, xerrors.IsRetryObjectValid(err))
+ })
+}
+
+func TestCreateNodeRequest(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ path string
+ config coordination.NodeConfig
+ operationParams *Ydb_Operations.OperationParams
+ request *Ydb_Coordination.CreateNodeRequest
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ path: "/abc",
+ config: coordination.NodeConfig{
+ Path: "/cde",
+ },
+ operationParams: operation.Params(context.Background(), time.Second, time.Second, operation.ModeSync),
+ request: &Ydb_Coordination.CreateNodeRequest{
+ Path: "/abc",
+ Config: &Ydb_Coordination.Config{
+ Path: "/cde",
+ },
+ OperationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ OperationTimeout: durationpb.New(time.Second),
+ CancelAfter: durationpb.New(time.Second),
+ },
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ request := createNodeRequest(tt.path, tt.config, tt.operationParams)
+ require.EqualValues(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
+ })
+ }
+}
+
+func TestDescribeNodeRequest(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ path string
+ operationParams *Ydb_Operations.OperationParams
+ request *Ydb_Coordination.DescribeNodeRequest
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ path: "/a/b/c",
+ operationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ },
+ request: &Ydb_Coordination.DescribeNodeRequest{
+ Path: "/a/b/c",
+ OperationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ },
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ request := describeNodeRequest(tt.path, tt.operationParams)
+ require.Equal(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
+ })
+ }
+}
+
+func TestOperationParams(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ ctx context.Context
+ config interface {
+ OperationTimeout() time.Duration
+ OperationCancelAfter() time.Duration
+ }
+ mode operation.Mode
+ operationParams *Ydb_Operations.OperationParams
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ ctx: context.Background(),
+ config: config.New(config.WithOperationCancelAfter(time.Second), config.WithOperationTimeout(time.Second)),
+ mode: operation.ModeSync,
+ operationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ OperationTimeout: durationpb.New(time.Second),
+ CancelAfter: durationpb.New(time.Second),
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ ctx: operation.WithCancelAfter(operation.WithTimeout(context.Background(), time.Second), time.Second),
+ config: config.New(),
+ mode: operation.ModeSync,
+ operationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ OperationTimeout: durationpb.New(time.Second),
+ CancelAfter: durationpb.New(time.Second),
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ params := operationParams(tt.ctx, tt.config, tt.mode)
+ require.Equal(t, xtest.ToJSON(tt.operationParams), xtest.ToJSON(params))
+ })
+ }
+}
+
+func TestDescribeNode(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().DescribeNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.DescribeNodeResponse{
+ Operation: &Ydb_Operations.Operation{
+ Ready: true,
+ Status: Ydb.StatusIds_SUCCESS,
+ Result: func() *anypb.Any {
+ result, err := anypb.New(&Ydb_Coordination.DescribeNodeResult{
+ Self: &Ydb_Scheme.Entry{
+ Name: "/a/b/c",
+ Owner: "root",
+ Type: Ydb_Scheme.Entry_COORDINATION_NODE,
+ },
+ Config: &Ydb_Coordination.Config{
+ Path: "/a/b/c",
+ SelfCheckPeriodMillis: 100,
+ SessionGracePeriodMillis: 1000,
+ ReadConsistencyMode: Ydb_Coordination.ConsistencyMode_CONSISTENCY_MODE_STRICT,
+ AttachConsistencyMode: Ydb_Coordination.ConsistencyMode_CONSISTENCY_MODE_STRICT,
+ RateLimiterCountersMode: Ydb_Coordination.RateLimiterCountersMode_RATE_LIMITER_COUNTERS_MODE_AGGREGATED,
+ },
+ })
+ require.NoError(t, err)
+
+ return result
+ }(),
+ },
+ }, nil)
+ nodeScheme, nodeConfig, err := describeNode(ctx, client, &Ydb_Coordination.DescribeNodeRequest{
+ Path: "/a/b/c",
+ OperationParams: nil,
+ })
+ require.NoError(t, err)
+ require.Equal(t, xtest.ToJSON(&scheme.Entry{
+ Name: "/a/b/c",
+ Owner: "root",
+ Type: scheme.EntryCoordinationNode,
+ }), xtest.ToJSON(nodeScheme))
+ require.Equal(t, xtest.ToJSON(coordination.NodeConfig{
+ Path: "/a/b/c",
+ SelfCheckPeriodMillis: 100,
+ SessionGracePeriodMillis: 1000,
+ ReadConsistencyMode: coordination.ConsistencyModeStrict,
+ AttachConsistencyMode: coordination.ConsistencyModeStrict,
+ RatelimiterCountersMode: coordination.RatelimiterCountersModeAggregated,
+ }), xtest.ToJSON(nodeConfig))
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().DescribeNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Transport(grpcStatus.Error(grpcCodes.Unavailable, "")),
+ )
+ nodeScheme, nodeConfig, err := describeNode(ctx, client, &Ydb_Coordination.DescribeNodeRequest{
+ Path: "/a/b/c",
+ OperationParams: nil,
+ })
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ require.Nil(t, nodeScheme)
+ require.Nil(t, nodeConfig)
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().DescribeNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ nodeScheme, nodeConfig, err := describeNode(ctx, client, &Ydb_Coordination.DescribeNodeRequest{
+ Path: "/a/b/c",
+ OperationParams: nil,
+ })
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ require.Nil(t, nodeScheme)
+ require.Nil(t, nodeConfig)
+ })
+}
+
+func TestAlterNodeRequest(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ path string
+ config coordination.NodeConfig
+ operationParams *Ydb_Operations.OperationParams
+ request *Ydb_Coordination.AlterNodeRequest
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ path: "/a/b/c",
+ config: coordination.NodeConfig{
+ Path: "/a/b/c",
+ },
+ operationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ },
+ request: &Ydb_Coordination.AlterNodeRequest{
+ Path: "/a/b/c",
+ Config: &Ydb_Coordination.Config{
+ Path: "/a/b/c",
+ },
+ OperationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ },
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ request := alterNodeRequest(tt.path, tt.config, tt.operationParams)
+ require.Equal(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
+ })
+ }
+}
+
+func TestAlterNode(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().AlterNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.AlterNodeResponse{
+ Operation: &Ydb_Operations.Operation{
+ Ready: true,
+ Status: Ydb.StatusIds_SUCCESS,
+ },
+ }, nil)
+ err := alterNode(ctx, client, &Ydb_Coordination.AlterNodeRequest{})
+ require.NoError(t, err)
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().AlterNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Transport(grpcStatus.Error(grpcCodes.ResourceExhausted, "")),
+ )
+ err := alterNode(ctx, client, &Ydb_Coordination.AlterNodeRequest{})
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.ResourceExhausted))
+ require.True(t, xerrors.IsRetryObjectValid(err))
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().AlterNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ err := alterNode(ctx, client, &Ydb_Coordination.AlterNodeRequest{})
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ require.True(t, xerrors.IsRetryObjectValid(err))
+ })
+}
+
+func TestDropNodeRequest(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ path string
+ operationParams *Ydb_Operations.OperationParams
+ request *Ydb_Coordination.DropNodeRequest
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ path: "/a/b/c",
+ operationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ },
+ request: &Ydb_Coordination.DropNodeRequest{
+ Path: "/a/b/c",
+ OperationParams: &Ydb_Operations.OperationParams{
+ OperationMode: Ydb_Operations.OperationParams_SYNC,
+ },
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ request := dropNodeRequest(tt.path, tt.operationParams)
+ require.Equal(t, xtest.ToJSON(tt.request), xtest.ToJSON(request))
+ })
+ }
+}
+
+func TestDropNode(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().DropNode(gomock.Any(), gomock.Any()).Return(&Ydb_Coordination.DropNodeResponse{
+ Operation: &Ydb_Operations.Operation{
+ Ready: true,
+ Status: Ydb.StatusIds_SUCCESS,
+ },
+ }, nil)
+ err := dropNode(ctx, client, &Ydb_Coordination.DropNodeRequest{})
+ require.NoError(t, err)
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().DropNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Transport(grpcStatus.Error(grpcCodes.ResourceExhausted, "")),
+ )
+ err := dropNode(ctx, client, &Ydb_Coordination.DropNodeRequest{})
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.ResourceExhausted))
+ require.True(t, xerrors.IsRetryObjectValid(err))
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ client := NewMockCoordinationServiceClient(ctrl)
+ client.EXPECT().DropNode(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ err := dropNode(ctx, client, &Ydb_Coordination.DropNodeRequest{})
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ require.True(t, xerrors.IsRetryObjectValid(err))
+ })
+}
diff --git a/internal/coordination/config/config.go b/internal/coordination/config/config.go
index a4525bfd1..692777a43 100644
--- a/internal/coordination/config/config.go
+++ b/internal/coordination/config/config.go
@@ -6,8 +6,6 @@ import (
)
// Config is an configuration of coordination client
-//
-//nolint:maligned
type Config struct {
config.Common
@@ -22,9 +20,9 @@ func (c Config) Trace() *trace.Coordination {
type Option func(c *Config)
// WithTrace appends coordination trace to early defined traces
-func WithTrace(trace trace.Coordination, opts ...trace.CoordinationComposeOption) Option {
+func WithTrace(trace *trace.Coordination, opts ...trace.CoordinationComposeOption) Option {
return func(c *Config) {
- c.trace = c.trace.Compose(&trace, opts...)
+ c.trace = c.trace.Compose(trace, opts...)
}
}
@@ -39,9 +37,9 @@ func New(opts ...Option) Config {
c := Config{
trace: &trace.Coordination{},
}
- for _, o := range opts {
- if o != nil {
- o(&c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&c)
}
}
diff --git a/internal/coordination/conversation/conversation.go b/internal/coordination/conversation/conversation.go
new file mode 100644
index 000000000..c3c8e1863
--- /dev/null
+++ b/internal/coordination/conversation/conversation.go
@@ -0,0 +1,517 @@
+// Package conversation contains coordination session internal code that helps implement a typical conversation-like
+// session protocol based on a bidirectional gRPC stream.
+package conversation
+
+import (
+ "context"
+ "sync"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+)
+
+// Controller provides a simple mechanism to work with a session protocol using a gRPC bidirectional stream. Creating a
+// bidirectional stream client may be quite tricky because messages are usually being processed independently and in
+// parallel. Moreover, the gRPC client library puts strict limitations on an implementation of the client, e.g. multiple
+// calls of the Send or Recv methods of the stub client must not be performed from different goroutines. Also, there are
+// no guarantees that a message successfully dispatched by the Send method will actually reach the server, neither does
+// the server enjoy same guarantees when delivering messages to the client. This usually ends up having two goroutines
+// (one for sending outgoing messages and another one for receiving incoming ones) and a queue where messages are
+// published to be eventually delivered to the server. The Controller simplifies working with this model providing
+// generic implementation of the message queue and related routines, handling retries of sent and pending operations
+// when the underlying gRPC stream needs to be reconnected.
+//
+// A typical coordination session looks like this (we are going to skip for now how the gRPC stream is created, handled
+// and kept alive, you can find the details on that in the Session, and focus on the protocol):
+//
+// 1. The client opens a new gRPC bidirectional stream.
+// 2. The client sends the SessionStart request and wait until the Failure or the SessionStarted reply.
+// 3. The server sends the SessionStarted response with the SessionID. At this point the session is started. If the
+// client needs to reconnect the gRPC stream in the future, it will use that SessionID to attach to the previously
+// created session in the SessionStart request.
+// 4. The client sends the AcquireSemaphore request to acquire a permit to the semaphore in this session with count 5.
+// 5. After a moment, the client decides to acquire another semaphore, it sends one more AcquireSemaphore request with
+// count 4.
+// 6. The server replies with the AcquireSemaphoreResult response to the second AcquireSemaphore request to inform the
+// client that the semaphore was successfully acquired.
+// 7. The server replies with the AcquireSemaphorePending response in order to inform the client that the semaphore
+// from the first request has been acquired by another session.
+// 8. After a while, the server sends the AcquireSemaphoreResult response which implies that the semaphore from the
+// first request is acquired in the current session.
+// 9. Then the client sends the ReleaseSemaphore request in order to release the acquired semaphore.
+// 10. The server replies with the ReleaseSemaphoreResult.
+// 11. The client terminates the session with the SessionStop request.
+// 12. The server let the client know that the session is over sending the SessionStopped response and closing the gRPC
+// stream.
+//
+// We can notice five independent conversations here:
+//
+// 1. StartSession, SessionStarted — points 2–3;
+// 2. AcquireSemaphore, AcquireSemaphoreResult — points 4, 6;
+// 3. AcquireSemaphore, AcquireSemaphorePending, AcquireSemaphoreResult — points 5, 7 and 8;
+// 4. ReleaseSemaphore, ReleaseSemaphoreResult — points 9–10;
+// 5. SessionStop, SessionStopped — points 11–12.
+//
+// If at any time the client encounters an unrecoverable error (for example, the underlying gRPC stream becomes
+// disconnected), the client will have to replay every conversation from their very beginning. Let us see why it is
+// actually the case. But before we go into that, let us look at the grpc.ClientStream SendMsg method:
+//
+// "…SendMsg does not wait until the message is received by the server. An untimely stream closure may result in lost
+// messages. To ensure delivery, users should ensure the RPC completed successfully using RecvMsg…"
+//
+// This is true for both, the client and the server. So when the server replies to the client it does not really know if
+// the response is received by the client. And vice versa, when the client sends a request to the server it has no way
+// to know if the request was delivered to the server unless the server sends another message to the client in reply.
+//
+// That is why conversation-like protocols typically use idempotent requests. Idempotent requests can be safely retried
+// as long as you keep the original order of the conversations. For example, if the gRPC stream is terminated before
+// the point 6, we cannot know if the server gets the requests. There may be one, two or none AcquireSemaphore requests
+// successfully delivered to and handled by the server. Moreover, the server may have already sent to the client the
+// corresponding responses. Nevertheless, if the requests are idempotent, we can safely retry them all in the newly
+// created gRPC stream and get the same results as we would have got if we had sent them without stream termination.
+// Note that if the stream is terminated before the point 8, we still need to replay the first AcquireSemaphore
+// conversation because we have no knowledge if the server replied with the AcquireSemaphoreResult in the terminated
+// stream.
+//
+// However, sometimes even idempotent requests cannot be safely retried. Consider the case wherein the point 5 from the
+// original list is:
+//
+// 5. After a moment, the client decides to modify the AcquireSemaphore request and sends another one with the same
+// semaphore but with count 4.
+//
+// If then the gRPC stream terminates, there are two likely outcomes:
+//
+// 1. The server received the first request but the second one was not delivered. The current semaphore count is 5.
+// 2. The server received and processed the both requests. The current semaphore permit count is 4.
+//
+// If we retry the both requests, the observed result will be different depending on which outcome occurs:
+//
+// 1. The first retry will be a noop, the second one will decrease the semaphore count to 4. This is expected behavior.
+// 2. The first retry will try to increase the semaphore count to 5, it causes an error. This is unexpected.
+//
+// In order to avoid that we could postpone a conversation if there is another one for the same semaphore which has been
+// sent but has not been yet delivered to the server. For more details, see the WithConflictKey option.
+type Controller struct {
+ mutex sync.Mutex // guards access to the fields below
+
+ queue []*Conversation // the message queue, the front is in the end of the slice
+ conflicts map[string]struct{}
+
+ notifyChan chan struct{}
+ closed bool
+}
+
+// ResponseFilter defines the filter function called by the controller to know if a received message relates to the
+// conversation. If a ResponseFilter returns true, the message is considered to be part of the conversation.
+type ResponseFilter func(request *Ydb_Coordination.SessionRequest, response *Ydb_Coordination.SessionResponse) bool
+
+// Conversation is a core concept of the conversation package. It is an ordered sequence of connected request/reply
+// messages. For example, the acquiring semaphore conversation may look like this:
+//
+// 1. The client sends the AcquireSemaphore request.
+// 2. The server replies with the AcquireSemaphorePending response.
+// 3. After a while, the server replies with the AcquireSemaphoreResult response. The conversation is ended.
+//
+// There may be many different conversations carried out simultaneously in one session, so the exact order of all the
+// messages in the session is unspecified. In the example above, there may be other messages (from different
+// conversations) between points 1 and 2, or 2 and 3.
+type Conversation struct {
+ message func() *Ydb_Coordination.SessionRequest
+ responseFilter ResponseFilter
+ acknowledgeFilter ResponseFilter
+ cancelMessage func(req *Ydb_Coordination.SessionRequest) *Ydb_Coordination.SessionRequest
+ cancelFilter ResponseFilter
+ conflictKey string
+ requestSent *Ydb_Coordination.SessionRequest
+ cancelRequestSent *Ydb_Coordination.SessionRequest
+ response *Ydb_Coordination.SessionResponse
+ responseErr error
+ done chan struct{}
+ idempotent bool
+ canceled bool
+}
+
+// NewController creates a new conversation controller. You usually have one controller per one session.
+func NewController() *Controller {
+ return &Controller{
+ notifyChan: make(chan struct{}, 1),
+ conflicts: make(map[string]struct{}),
+ }
+}
+
+// WithResponseFilter returns an Option that specifies the filter function that is used to detect the last response
+// message in the conversation. If such a message was found, the conversation is immediately ended and the response
+// becomes available by the Conversation.Await method.
+func WithResponseFilter(filter ResponseFilter) Option {
+ return func(c *Conversation) {
+ c.responseFilter = filter
+ }
+}
+
+// WithAcknowledgeFilter returns an Option that specifies the filter function that is used to detect an intermediate
+// response message in the conversation. If such a message was found, the conversation continues, but it lets the client
+// know that the server successfully consumed the first request of the conversation.
+func WithAcknowledgeFilter(filter ResponseFilter) Option {
+ return func(c *Conversation) {
+ c.acknowledgeFilter = filter
+ }
+}
+
+// WithCancelMessage returns an Option that specifies the message and filter functions that are used to cancel the
+// conversation which has been already sent. This message is sent in the background when the caller cancels the context
+// of the Controller.Await function. The response is never received by the caller and is only used to end the
+// conversation and remove it from the queue.
+func WithCancelMessage(
+ message func(req *Ydb_Coordination.SessionRequest) *Ydb_Coordination.SessionRequest,
+ filter ResponseFilter,
+) Option {
+ return func(c *Conversation) {
+ c.cancelMessage = message
+ c.cancelFilter = filter
+ }
+}
+
+// WithConflictKey returns an Option that specifies the key that is used to find out messages that cannot be delivered
+// to the server until the server acknowledged the request. If there is a conversation with the same conflict key in the
+// queue that has not been yet delivered to the server, the controller will temporarily suspend other conversations with
+// the same conflict key until the first one is acknowledged.
+func WithConflictKey(key string) Option {
+ return func(c *Conversation) {
+ c.conflictKey = key
+ }
+}
+
+// WithIdempotence returns an Option that enabled retries for this conversation when the underlying gRPC stream
+// reconnects. The controller will replay the whole conversation from scratch unless it is not ended.
+func WithIdempotence(idempotent bool) Option {
+ return func(c *Conversation) {
+ c.idempotent = idempotent
+ }
+}
+
+// Option configures how we create a new conversation.
+type Option func(c *Conversation)
+
+// NewConversation creates a new conversation that starts with a specified message.
+func NewConversation(request func() *Ydb_Coordination.SessionRequest, opts ...Option) *Conversation {
+ conversation := Conversation{message: request}
+ for _, o := range opts {
+ if o != nil {
+ o(&conversation)
+ }
+ }
+
+ return &conversation
+}
+
+func (c *Controller) notify() {
+ select {
+ case c.notifyChan <- struct{}{}:
+ default:
+ }
+}
+
+// PushBack puts a new conversation at the end of the queue.
+func (c *Controller) PushBack(conversation *Conversation) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ if c.closed {
+ return coordination.ErrSessionClosed
+ }
+
+ conversation.enqueue()
+ c.queue = append([]*Conversation{conversation}, c.queue...)
+ c.notify()
+
+ return nil
+}
+
+// PushFront puts a new conversation at the beginning of the queue.
+func (c *Controller) PushFront(conversation *Conversation) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ if c.closed {
+ return coordination.ErrSessionClosed
+ }
+
+ conversation.enqueue()
+ c.queue = append(c.queue, conversation)
+ c.notify()
+
+ return nil
+}
+
+func (c *Controller) sendFront() *Ydb_Coordination.SessionRequest {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ // We are notified but there are no conversations in the queue. Return nil to make the loop in OnSend wait.
+ if len(c.queue) == 0 {
+ return nil
+ }
+
+ for i := len(c.queue) - 1; i >= 0; i-- {
+ req := c.queue[i]
+
+ if req.canceled && req.cancelRequestSent == nil {
+ req.sendCancel()
+ c.notify()
+
+ return req.cancelRequestSent
+ }
+
+ if req.requestSent != nil {
+ continue
+ }
+
+ if _, ok := c.conflicts[req.conflictKey]; ok {
+ continue
+ }
+
+ req.send()
+
+ if req.conflictKey != "" {
+ c.conflicts[req.conflictKey] = struct{}{}
+ }
+ if req.responseFilter == nil && req.acknowledgeFilter == nil {
+ c.queue = append(c.queue[:i], c.queue[i+1:]...)
+ }
+ c.notify()
+
+ return req.requestSent
+ }
+
+ return nil
+}
+
+// OnSend blocks until a new conversation request becomes available at the end of the queue. You should call this method
+// in the goroutine that handles gRPC stream Send method. ctx can be used to cancel the call.
+func (c *Controller) OnSend(ctx context.Context) (*Ydb_Coordination.SessionRequest, error) {
+ var req *Ydb_Coordination.SessionRequest
+ for {
+ select {
+ case <-ctx.Done():
+ case <-c.notifyChan:
+ req = c.sendFront()
+ }
+
+ // Process ctx.Done() first to make sure we cancel the call if conversations are too chatty.
+ if ctx.Err() != nil {
+ return nil, ctx.Err()
+ }
+
+ // We were notified but there were no messages in the queue. Just wait for more messages become available.
+ if req != nil {
+ break
+ }
+ }
+
+ return req, nil
+}
+
+// OnRecv consumes a new conversation response and process with the corresponding conversation if any exists for it. The
+// returned value indicates if any conversation considers the incoming message part of it or the controller is closed.
+// You should call this method in the goroutine that handles gRPC stream Recv method.
+func (c *Controller) OnRecv(resp *Ydb_Coordination.SessionResponse) bool {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ notify := false
+ handled := false
+ for i := len(c.queue) - 1; i >= 0; i-- {
+ req := c.queue[i]
+ if req.requestSent == nil {
+ continue
+ }
+
+ switch {
+ case req.responseFilter != nil && req.responseFilter(req.requestSent, resp):
+ if !req.canceled {
+ req.succeed(resp)
+
+ if req.conflictKey != "" {
+ delete(c.conflicts, req.conflictKey)
+ notify = true
+ }
+
+ c.queue = append(c.queue[:i], c.queue[i+1:]...)
+ }
+
+ handled = true
+ case req.acknowledgeFilter != nil && req.acknowledgeFilter(req.requestSent, resp):
+ if !req.canceled {
+ if req.conflictKey != "" {
+ delete(c.conflicts, req.conflictKey)
+ notify = true
+ }
+ }
+
+ handled = true
+ case req.cancelRequestSent != nil && req.cancelFilter(req.cancelRequestSent, resp):
+ if req.conflictKey != "" {
+ delete(c.conflicts, req.conflictKey)
+ notify = true
+ }
+ c.queue = append(c.queue[:i], c.queue[i+1:]...)
+ handled = true
+ }
+ }
+
+ if notify {
+ c.notify()
+ }
+
+ return c.closed || handled
+}
+
+// OnDetach fails all non-idempotent conversations if there are any in the queue. You should call this method when the
+// underlying gRPC stream of the session is closed.
+func (c *Controller) OnDetach() {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ for i := len(c.queue) - 1; i >= 0; i-- {
+ req := c.queue[i]
+ if !req.idempotent {
+ req.fail(coordination.ErrOperationStatusUnknown)
+
+ if req.requestSent != nil && req.conflictKey != "" {
+ delete(c.conflicts, req.conflictKey)
+ }
+
+ c.queue = append(c.queue[:i], c.queue[i+1:]...)
+ }
+ }
+}
+
+// Close fails all conversations if there are any in the queue. It also does not allow pushing more conversations to the
+// queue anymore. You may optionally specify the final conversation if needed.
+func (c *Controller) Close(byeConversation *Conversation) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ c.closed = true
+
+ for i := len(c.queue) - 1; i >= 0; i-- {
+ req := c.queue[i]
+ if !req.canceled {
+ req.fail(coordination.ErrSessionClosed)
+ }
+ }
+
+ if byeConversation != nil {
+ byeConversation.enqueue()
+ c.queue = []*Conversation{byeConversation}
+ }
+
+ c.notify()
+}
+
+// OnAttach retries all idempotent conversations if there are any in the queue. You should call this method when the
+// underlying gRPC stream of the session is connected.
+func (c *Controller) OnAttach() {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ notify := false
+ for i := len(c.queue) - 1; i >= 0; i-- {
+ req := c.queue[i]
+ if req.idempotent && req.requestSent != nil {
+ if req.conflictKey != "" {
+ delete(c.conflicts, req.conflictKey)
+ }
+
+ req.requestSent = nil
+ notify = true
+ }
+ }
+
+ if notify {
+ c.notify()
+ }
+}
+
+// Cancel the conversation if it has been sent and there is no response ready. This returns false if the response is
+// ready and the caller may safely return it instead of canceling the conversation.
+func (c *Controller) cancel(conversation *Conversation) bool {
+ if conversation.cancelMessage == nil {
+ return true
+ }
+
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ // The context is canceled but the response is ready, return it anyway.
+ if conversation.response != nil || conversation.responseErr != nil {
+ return false
+ }
+
+ if conversation.requestSent != nil {
+ conversation.cancel()
+ c.notify()
+ } else {
+ // If the response has not been sent, just remove it from the queue.
+ for i := len(c.queue) - 1; i >= 0; i-- {
+ req := c.queue[i]
+ if req == conversation {
+ c.queue = append(c.queue[:i], c.queue[i+1:]...)
+
+ break
+ }
+ }
+ }
+
+ return true
+}
+
+// Await waits until the conversation ends. ctx can be used to cancel the call.
+func (c *Controller) Await(
+ ctx context.Context,
+ conversation *Conversation,
+) (*Ydb_Coordination.SessionResponse, error) {
+ select {
+ case <-conversation.done:
+ case <-ctx.Done():
+ }
+
+ if ctx.Err() != nil && c.cancel(conversation) {
+ return nil, ctx.Err()
+ }
+
+ if conversation.responseErr != nil {
+ return nil, conversation.responseErr
+ }
+
+ return conversation.response, nil
+}
+
+func (c *Conversation) enqueue() {
+ c.requestSent = nil
+ c.done = make(chan struct{})
+}
+
+func (c *Conversation) send() {
+ c.requestSent = c.message()
+}
+
+func (c *Conversation) sendCancel() {
+ c.cancelRequestSent = c.cancelMessage(c.requestSent)
+}
+
+func (c *Conversation) succeed(response *Ydb_Coordination.SessionResponse) {
+ c.response = response
+ close(c.done)
+}
+
+func (c *Conversation) fail(err error) {
+ c.responseErr = err
+ close(c.done)
+}
+
+func (c *Conversation) cancel() {
+ c.canceled = true
+ close(c.done)
+}
diff --git a/internal/coordination/grpc_client_mock_test.go b/internal/coordination/grpc_client_mock_test.go
new file mode 100644
index 000000000..3c2d2a711
--- /dev/null
+++ b/internal/coordination/grpc_client_mock_test.go
@@ -0,0 +1,278 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1 (interfaces: CoordinationServiceClient,CoordinationService_SessionClient)
+//
+// Generated by this command:
+//
+// mockgen -destination grpc_client_mock_test.go -package coordination -write_package_comment=false github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1 CoordinationServiceClient,CoordinationService_SessionClient
+package coordination
+
+import (
+ context "context"
+ reflect "reflect"
+
+ Ydb_Coordination_V1 "github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1"
+ Ydb_Coordination "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+ gomock "go.uber.org/mock/gomock"
+ grpc "google.golang.org/grpc"
+ metadata "google.golang.org/grpc/metadata"
+)
+
+// MockCoordinationServiceClient is a mock of CoordinationServiceClient interface.
+type MockCoordinationServiceClient struct {
+ ctrl *gomock.Controller
+ recorder *MockCoordinationServiceClientMockRecorder
+}
+
+// MockCoordinationServiceClientMockRecorder is the mock recorder for MockCoordinationServiceClient.
+type MockCoordinationServiceClientMockRecorder struct {
+ mock *MockCoordinationServiceClient
+}
+
+// NewMockCoordinationServiceClient creates a new mock instance.
+func NewMockCoordinationServiceClient(ctrl *gomock.Controller) *MockCoordinationServiceClient {
+ mock := &MockCoordinationServiceClient{ctrl: ctrl}
+ mock.recorder = &MockCoordinationServiceClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockCoordinationServiceClient) EXPECT() *MockCoordinationServiceClientMockRecorder {
+ return m.recorder
+}
+
+// AlterNode mocks base method.
+func (m *MockCoordinationServiceClient) AlterNode(arg0 context.Context, arg1 *Ydb_Coordination.AlterNodeRequest, arg2 ...grpc.CallOption) (*Ydb_Coordination.AlterNodeResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "AlterNode", varargs...)
+ ret0, _ := ret[0].(*Ydb_Coordination.AlterNodeResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// AlterNode indicates an expected call of AlterNode.
+func (mr *MockCoordinationServiceClientMockRecorder) AlterNode(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AlterNode", reflect.TypeOf((*MockCoordinationServiceClient)(nil).AlterNode), varargs...)
+}
+
+// CreateNode mocks base method.
+func (m *MockCoordinationServiceClient) CreateNode(arg0 context.Context, arg1 *Ydb_Coordination.CreateNodeRequest, arg2 ...grpc.CallOption) (*Ydb_Coordination.CreateNodeResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "CreateNode", varargs...)
+ ret0, _ := ret[0].(*Ydb_Coordination.CreateNodeResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// CreateNode indicates an expected call of CreateNode.
+func (mr *MockCoordinationServiceClientMockRecorder) CreateNode(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNode", reflect.TypeOf((*MockCoordinationServiceClient)(nil).CreateNode), varargs...)
+}
+
+// DescribeNode mocks base method.
+func (m *MockCoordinationServiceClient) DescribeNode(arg0 context.Context, arg1 *Ydb_Coordination.DescribeNodeRequest, arg2 ...grpc.CallOption) (*Ydb_Coordination.DescribeNodeResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "DescribeNode", varargs...)
+ ret0, _ := ret[0].(*Ydb_Coordination.DescribeNodeResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DescribeNode indicates an expected call of DescribeNode.
+func (mr *MockCoordinationServiceClientMockRecorder) DescribeNode(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNode", reflect.TypeOf((*MockCoordinationServiceClient)(nil).DescribeNode), varargs...)
+}
+
+// DropNode mocks base method.
+func (m *MockCoordinationServiceClient) DropNode(arg0 context.Context, arg1 *Ydb_Coordination.DropNodeRequest, arg2 ...grpc.CallOption) (*Ydb_Coordination.DropNodeResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "DropNode", varargs...)
+ ret0, _ := ret[0].(*Ydb_Coordination.DropNodeResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DropNode indicates an expected call of DropNode.
+func (mr *MockCoordinationServiceClientMockRecorder) DropNode(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropNode", reflect.TypeOf((*MockCoordinationServiceClient)(nil).DropNode), varargs...)
+}
+
+// Session mocks base method.
+func (m *MockCoordinationServiceClient) Session(arg0 context.Context, arg1 ...grpc.CallOption) (Ydb_Coordination_V1.CoordinationService_SessionClient, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0}
+ for _, a := range arg1 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "Session", varargs...)
+ ret0, _ := ret[0].(Ydb_Coordination_V1.CoordinationService_SessionClient)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Session indicates an expected call of Session.
+func (mr *MockCoordinationServiceClientMockRecorder) Session(arg0 any, arg1 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0}, arg1...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Session", reflect.TypeOf((*MockCoordinationServiceClient)(nil).Session), varargs...)
+}
+
+// MockCoordinationService_SessionClient is a mock of CoordinationService_SessionClient interface.
+type MockCoordinationService_SessionClient struct {
+ ctrl *gomock.Controller
+ recorder *MockCoordinationService_SessionClientMockRecorder
+}
+
+// MockCoordinationService_SessionClientMockRecorder is the mock recorder for MockCoordinationService_SessionClient.
+type MockCoordinationService_SessionClientMockRecorder struct {
+ mock *MockCoordinationService_SessionClient
+}
+
+// NewMockCoordinationService_SessionClient creates a new mock instance.
+func NewMockCoordinationService_SessionClient(ctrl *gomock.Controller) *MockCoordinationService_SessionClient {
+ mock := &MockCoordinationService_SessionClient{ctrl: ctrl}
+ mock.recorder = &MockCoordinationService_SessionClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockCoordinationService_SessionClient) EXPECT() *MockCoordinationService_SessionClientMockRecorder {
+ return m.recorder
+}
+
+// CloseSend mocks base method.
+func (m *MockCoordinationService_SessionClient) CloseSend() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CloseSend")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// CloseSend indicates an expected call of CloseSend.
+func (mr *MockCoordinationService_SessionClientMockRecorder) CloseSend() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).CloseSend))
+}
+
+// Context mocks base method.
+func (m *MockCoordinationService_SessionClient) Context() context.Context {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Context")
+ ret0, _ := ret[0].(context.Context)
+ return ret0
+}
+
+// Context indicates an expected call of Context.
+func (mr *MockCoordinationService_SessionClientMockRecorder) Context() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).Context))
+}
+
+// Header mocks base method.
+func (m *MockCoordinationService_SessionClient) Header() (metadata.MD, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Header")
+ ret0, _ := ret[0].(metadata.MD)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Header indicates an expected call of Header.
+func (mr *MockCoordinationService_SessionClientMockRecorder) Header() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).Header))
+}
+
+// Recv mocks base method.
+func (m *MockCoordinationService_SessionClient) Recv() (*Ydb_Coordination.SessionResponse, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Recv")
+ ret0, _ := ret[0].(*Ydb_Coordination.SessionResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Recv indicates an expected call of Recv.
+func (mr *MockCoordinationService_SessionClientMockRecorder) Recv() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).Recv))
+}
+
+// RecvMsg mocks base method.
+func (m *MockCoordinationService_SessionClient) RecvMsg(arg0 any) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "RecvMsg", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// RecvMsg indicates an expected call of RecvMsg.
+func (mr *MockCoordinationService_SessionClientMockRecorder) RecvMsg(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).RecvMsg), arg0)
+}
+
+// Send mocks base method.
+func (m *MockCoordinationService_SessionClient) Send(arg0 *Ydb_Coordination.SessionRequest) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Send", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Send indicates an expected call of Send.
+func (mr *MockCoordinationService_SessionClientMockRecorder) Send(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).Send), arg0)
+}
+
+// SendMsg mocks base method.
+func (m *MockCoordinationService_SessionClient) SendMsg(arg0 any) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SendMsg", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendMsg indicates an expected call of SendMsg.
+func (mr *MockCoordinationService_SessionClientMockRecorder) SendMsg(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).SendMsg), arg0)
+}
+
+// Trailer mocks base method.
+func (m *MockCoordinationService_SessionClient) Trailer() metadata.MD {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Trailer")
+ ret0, _ := ret[0].(metadata.MD)
+ return ret0
+}
+
+// Trailer indicates an expected call of Trailer.
+func (mr *MockCoordinationService_SessionClientMockRecorder) Trailer() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockCoordinationService_SessionClient)(nil).Trailer))
+}
diff --git a/internal/coordination/session.go b/internal/coordination/session.go
new file mode 100644
index 000000000..af10c064c
--- /dev/null
+++ b/internal/coordination/session.go
@@ -0,0 +1,891 @@
+package coordination
+
+import (
+ "context"
+ "encoding/binary"
+ "math"
+ "math/rand"
+ "sync"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination/conversation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+type session struct {
+ options *options.CreateSessionOptions
+ client *Client
+
+ ctx context.Context
+ cancel context.CancelFunc
+ sessionClosedChan chan struct{}
+ controller *conversation.Controller
+ sessionID uint64
+
+ mutex sync.Mutex // guards the field below
+ lastGoodResponseTime time.Time
+ cancelStream context.CancelFunc
+}
+
+type lease struct {
+ session *session
+ name string
+ ctx context.Context
+ cancel context.CancelFunc
+}
+
+func createSession(
+ ctx context.Context,
+ client *Client,
+ path string,
+ opts *options.CreateSessionOptions,
+) (*session, error) {
+ sessionCtx, cancel := xcontext.WithCancel(xcontext.ValueOnly(ctx))
+ s := session{
+ options: opts,
+ client: client,
+ ctx: sessionCtx,
+ cancel: cancel,
+ sessionClosedChan: make(chan struct{}),
+ controller: conversation.NewController(),
+ }
+ client.sessionCreated(&s)
+
+ sessionStartedChan := make(chan struct{})
+ go s.mainLoop(path, sessionStartedChan)
+
+ select {
+ case <-ctx.Done():
+ cancel()
+
+ return nil, ctx.Err()
+ case <-sessionStartedChan:
+ }
+
+ return &s, nil
+}
+
+func newProtectionKey() []byte {
+ key := make([]byte, 8)
+ binary.LittleEndian.PutUint64(key, rand.Uint64()) //nolint:gosec
+
+ return key
+}
+
+func newReqID() uint64 {
+ return rand.Uint64() //nolint:gosec
+}
+
+func (s *session) updateLastGoodResponseTime() {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ now := time.Now()
+
+ if now.After(s.lastGoodResponseTime) {
+ s.lastGoodResponseTime = now
+ }
+}
+
+func (s *session) getLastGoodResponseTime() time.Time {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ return s.lastGoodResponseTime
+}
+
+func (s *session) updateCancelStream(cancel context.CancelFunc) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ s.cancelStream = cancel
+}
+
+// Create a new gRPC stream using an independent context.
+func (s *session) newStream(
+ streamCtx context.Context,
+ cancelStream context.CancelFunc,
+) (Ydb_Coordination_V1.CoordinationService_SessionClient, error) {
+ // This deadline if final. If we have not got a session before it, the session is either expired or has never been
+ // created.
+ var deadline time.Time
+ if s.sessionID != 0 {
+ deadline = s.getLastGoodResponseTime().Add(s.options.SessionTimeout)
+ } else {
+ // Large enough to make the loop infinite, small enough to allow the maximum duration value (~290 years).
+ deadline = time.Now().Add(time.Hour * 24 * 365 * 100)
+ }
+
+ lastChance := false
+ for {
+ result := make(chan Ydb_Coordination_V1.CoordinationService_SessionClient, 1)
+ go func() {
+ var err error
+ onDone := trace.CoordinationOnStreamNew(s.client.config.Trace())
+ defer func() {
+ onDone(err)
+ }()
+
+ client, err := s.client.client.Session(streamCtx)
+ result <- client
+ }()
+
+ var client Ydb_Coordination_V1.CoordinationService_SessionClient
+ if lastChance {
+ timer := time.NewTimer(s.options.SessionKeepAliveTimeout)
+ select {
+ case <-timer.C:
+ case client = <-result:
+ }
+ timer.Stop()
+
+ if client != nil {
+ return client, nil
+ }
+
+ cancelStream()
+
+ return nil, s.ctx.Err()
+ }
+
+ // Since the deadline is probably large enough, avoid the timer leak with time.After.
+ timer := time.NewTimer(time.Until(deadline))
+ select {
+ case <-s.ctx.Done():
+ case client = <-result:
+ case <-timer.C:
+ trace.CoordinationOnSessionClientTimeout(
+ s.client.config.Trace(),
+ s.getLastGoodResponseTime(),
+ s.options.SessionTimeout,
+ )
+ cancelStream()
+
+ return nil, coordination.ErrSessionClosed
+ }
+ timer.Stop()
+
+ if client != nil {
+ return client, nil
+ }
+
+ // Waiting for some time before trying to reconnect.
+ sessionReconnectDelay := time.NewTimer(s.options.SessionReconnectDelay)
+ select {
+ case <-sessionReconnectDelay.C:
+ case <-s.ctx.Done():
+ }
+ sessionReconnectDelay.Stop()
+
+ if s.ctx.Err() != nil {
+ // Give this session the last chance to stop gracefully if the session is canceled in the reconnect cycle.
+ if s.sessionID != 0 {
+ lastChance = true
+ } else {
+ cancelStream()
+
+ return nil, s.ctx.Err()
+ }
+ }
+ }
+}
+
+func (s *session) mainLoop(path string, sessionStartedChan chan struct{}) {
+ defer s.client.sessionClosed(s)
+ defer close(s.sessionClosedChan)
+ defer s.cancel()
+
+ var seqNo uint64
+
+ protectionKey := newProtectionKey()
+ closing := false
+
+ for {
+ // Create a new grpc stream and start the receiver and sender loops.
+ //
+ // We use the stream context as a way to inform the main loop that the session must be reconnected if an
+ // unrecoverable error occurs in the receiver or sender loop. This also helps stop the other loop if an error
+ // is caught on only one of them.
+ //
+ // We intentionally place a stream context outside the scope of any existing contexts to make an attempt to
+ // close the session gracefully at the end of the main loop.
+
+ streamCtx, cancelStream := context.WithCancel(context.Background())
+ sessionClient, err := s.newStream(streamCtx, cancelStream)
+ if err != nil {
+ // Giving up, we can do nothing without a stream.
+ s.controller.Close(nil)
+
+ return
+ }
+
+ s.updateCancelStream(cancelStream)
+
+ // Start the loops.
+ wg := sync.WaitGroup{}
+ wg.Add(2)
+ sessionStarted := make(chan *Ydb_Coordination.SessionResponse_SessionStarted, 1)
+ sessionStopped := make(chan *Ydb_Coordination.SessionResponse_SessionStopped, 1)
+ startSending := make(chan struct{})
+ s.controller.OnAttach()
+
+ go s.receiveLoop(&wg, sessionClient, cancelStream, sessionStarted, sessionStopped)
+ go s.sendLoop(
+ &wg,
+ sessionClient,
+ streamCtx,
+ cancelStream,
+ startSending,
+ path,
+ protectionKey,
+ s.sessionID,
+ seqNo,
+ )
+
+ // Wait for the session started response unless the stream context is done. We intentionally do not take into
+ // account stream context cancellation in order to proceed with the graceful shutdown if it requires reconnect.
+ sessionStartTimer := time.NewTimer(s.options.SessionStartTimeout)
+ select {
+ case start := <-sessionStarted:
+ trace.CoordinationOnSessionStarted(s.client.config.Trace(), start.GetSessionId(), s.sessionID)
+ if s.sessionID == 0 {
+ s.sessionID = start.GetSessionId()
+ close(sessionStartedChan)
+ } else if start.GetSessionId() != s.sessionID {
+ // Reconnect if the server response is invalid.
+ cancelStream()
+ }
+ close(startSending)
+ case <-sessionStartTimer.C:
+ // Reconnect if no response was received before the timeout occurred.
+ trace.CoordinationOnSessionStartTimeout(s.client.config.Trace(), s.options.SessionStartTimeout)
+ cancelStream()
+ case <-streamCtx.Done():
+ case <-s.ctx.Done():
+ }
+ sessionStartTimer.Stop()
+
+ for {
+ // Respect the failure reason priority: if the session context is done, we must stop the session, even
+ // though the stream context may also be canceled.
+ if s.ctx.Err() != nil {
+ closing = true
+
+ break
+ }
+ if streamCtx.Err() != nil {
+ // Reconnect if an error occurred during the start session conversation.
+ break
+ }
+
+ keepAliveTime := time.Until(s.getLastGoodResponseTime().Add(s.options.SessionKeepAliveTimeout))
+ keepAliveTimeTimer := time.NewTimer(keepAliveTime)
+ select {
+ case <-keepAliveTimeTimer.C:
+ last := s.getLastGoodResponseTime()
+ if time.Since(last) > s.options.SessionKeepAliveTimeout {
+ // Reconnect if the underlying stream is likely to be dead.
+ trace.CoordinationOnSessionKeepAliveTimeout(
+ s.client.config.Trace(),
+ last,
+ s.options.SessionKeepAliveTimeout,
+ )
+ cancelStream()
+ }
+ case <-streamCtx.Done():
+ case <-s.ctx.Done():
+ }
+ keepAliveTimeTimer.Stop()
+ }
+
+ if closing {
+ // No need to stop the session if it was not started.
+ if s.sessionID == 0 {
+ s.controller.Close(nil)
+ cancelStream()
+
+ return
+ }
+
+ trace.CoordinationOnSessionStop(s.client.config.Trace(), s.sessionID)
+ s.controller.Close(conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_SessionStop_{
+ SessionStop: &Ydb_Coordination.SessionRequest_SessionStop{},
+ },
+ }
+ }),
+ )
+
+ // Wait for the session stopped response unless the stream context is done.
+ sessionStopTimeout := time.NewTimer(s.options.SessionStopTimeout)
+ select {
+ case stop := <-sessionStopped:
+ sessionStopTimeout.Stop()
+ trace.CoordinationOnSessionStopped(s.client.config.Trace(), stop.GetSessionId(), s.sessionID)
+ if stop.GetSessionId() == s.sessionID {
+ cancelStream()
+
+ return
+ }
+
+ // Reconnect if the server response is invalid.
+ cancelStream()
+ case <-sessionStopTimeout.C:
+ sessionStopTimeout.Stop() // no really need, call stop for common style only
+
+ // Reconnect if no response was received before the timeout occurred.
+ trace.CoordinationOnSessionStopTimeout(s.client.config.Trace(), s.options.SessionStopTimeout)
+ cancelStream()
+ case <-s.ctx.Done():
+ sessionStopTimeout.Stop()
+ cancelStream()
+
+ return
+ case <-streamCtx.Done():
+ sessionStopTimeout.Stop()
+ }
+ }
+
+ // Make sure no one is processing the stream anymore.
+ wg.Wait()
+
+ s.controller.OnDetach()
+ seqNo++
+ }
+}
+
+func (s *session) receiveLoop(
+ wg *sync.WaitGroup,
+ sessionClient Ydb_Coordination_V1.CoordinationService_SessionClient,
+ cancelStream context.CancelFunc,
+ sessionStarted chan *Ydb_Coordination.SessionResponse_SessionStarted,
+ sessionStopped chan *Ydb_Coordination.SessionResponse_SessionStopped,
+) {
+ // If the sendLoop is done, make sure the stream is also canceled to make the receiveLoop finish its work and cause
+ // reconnect.
+ defer wg.Done()
+ defer cancelStream()
+
+ for {
+ onDone := trace.CoordinationOnSessionReceive(s.client.config.Trace())
+ message, err := sessionClient.Recv()
+ if err != nil {
+ // Any stream error is unrecoverable, try to reconnect.
+ onDone(nil, err)
+
+ return
+ }
+ onDone(message, nil)
+
+ switch message.GetResponse().(type) {
+ case *Ydb_Coordination.SessionResponse_Failure_:
+ if message.GetFailure().GetStatus() == Ydb.StatusIds_SESSION_EXPIRED ||
+ message.GetFailure().GetStatus() == Ydb.StatusIds_UNAUTHORIZED ||
+ message.GetFailure().GetStatus() == Ydb.StatusIds_NOT_FOUND {
+ // Consider the session expired if we got an unrecoverable status.
+ trace.CoordinationOnSessionServerExpire(s.client.config.Trace(), message.GetFailure())
+
+ return
+ }
+
+ trace.CoordinationOnSessionServerError(s.client.config.Trace(), message.GetFailure())
+
+ return
+ case *Ydb_Coordination.SessionResponse_SessionStarted_:
+ sessionStarted <- message.GetSessionStarted()
+ s.updateLastGoodResponseTime()
+ case *Ydb_Coordination.SessionResponse_SessionStopped_:
+ sessionStopped <- message.GetSessionStopped()
+ s.cancel()
+
+ return
+ case *Ydb_Coordination.SessionResponse_Ping:
+ opaque := message.GetPing().GetOpaque()
+ err := s.controller.PushFront(conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_Pong{
+ Pong: &Ydb_Coordination.SessionRequest_PingPong{
+ Opaque: opaque,
+ },
+ },
+ }
+ }),
+ )
+ if err != nil {
+ // The session is closed if we cannot send the pong request back, so just exit the loop.
+ return
+ }
+ s.updateLastGoodResponseTime()
+ case *Ydb_Coordination.SessionResponse_Pong:
+ // Ignore pongs since we do not ping the server.
+ default:
+ if !s.controller.OnRecv(message) {
+ // Reconnect if the message is not from any known conversation.
+ trace.CoordinationOnSessionReceiveUnexpected(s.client.config.Trace(), message)
+
+ return
+ }
+
+ s.updateLastGoodResponseTime()
+ }
+ }
+}
+
+//nolint:revive
+func (s *session) sendLoop(
+ wg *sync.WaitGroup,
+ sessionClient Ydb_Coordination_V1.CoordinationService_SessionClient,
+ streamCtx context.Context,
+ cancelStream context.CancelFunc,
+ startSending chan struct{},
+ path string,
+ protectionKey []byte,
+ sessionID uint64,
+ seqNo uint64,
+) {
+ // If the sendLoop is done, make sure the stream is also canceled to make the receiveLoop finish its work and cause
+ // reconnect.
+ defer wg.Done()
+ defer cancelStream()
+
+ // Start a new session.
+ onDone := trace.CoordinationOnSessionStart(s.client.config.Trace())
+ startSession := Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_SessionStart_{
+ SessionStart: &Ydb_Coordination.SessionRequest_SessionStart{
+ Path: path,
+ SessionId: sessionID,
+ TimeoutMillis: uint64(s.options.SessionTimeout.Milliseconds()),
+ ProtectionKey: protectionKey,
+ SeqNo: seqNo,
+ Description: s.options.Description,
+ },
+ },
+ }
+ err := sessionClient.Send(&startSession)
+ if err != nil {
+ // Reconnect if a session cannot be started in this stream.
+ onDone(err)
+
+ return
+ }
+ onDone(nil)
+
+ // Wait for a response to the session start request in order to carry over the accumulated conversations until the
+ // server confirms that the session is running. This is not absolutely necessary but helps the client to not fail
+ // non-idempotent requests in case of the session handshake errors.
+ select {
+ case <-streamCtx.Done():
+ case <-startSending:
+ }
+
+ for {
+ message, err := s.controller.OnSend(streamCtx)
+ if err != nil {
+ return
+ }
+
+ onSendDone := trace.CoordinationOnSessionSend(s.client.config.Trace(), message)
+ err = sessionClient.Send(message)
+ if err != nil {
+ // Any stream error is unrecoverable, try to reconnect.
+ onSendDone(err)
+
+ return
+ }
+ onSendDone(nil)
+ }
+}
+
+func (s *session) Context() context.Context {
+ return s.ctx
+}
+
+func (s *session) Close(ctx context.Context) error {
+ s.cancel()
+
+ select {
+ case <-s.sessionClosedChan:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+
+ return nil
+}
+
+func (s *session) Reconnect() {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ if s.cancelStream != nil {
+ s.cancelStream()
+ }
+}
+
+func (s *session) SessionID() uint64 {
+ return s.sessionID
+}
+
+func (s *session) CreateSemaphore(
+ ctx context.Context,
+ name string,
+ limit uint64,
+ opts ...options.CreateSemaphoreOption,
+) error {
+ req := conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ createSemaphore := Ydb_Coordination.SessionRequest_CreateSemaphore{
+ ReqId: newReqID(),
+ Name: name,
+ Limit: limit,
+ }
+ for _, o := range opts {
+ if o != nil {
+ o(&createSemaphore)
+ }
+ }
+
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_CreateSemaphore_{
+ CreateSemaphore: &createSemaphore,
+ },
+ }
+ },
+ conversation.WithResponseFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetCreateSemaphoreResult().GetReqId() == request.GetCreateSemaphore().GetReqId()
+ }),
+ )
+ if err := s.controller.PushBack(req); err != nil {
+ return err
+ }
+
+ _, err := s.controller.Await(ctx, req)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *session) UpdateSemaphore(
+ ctx context.Context,
+ name string,
+ opts ...options.UpdateSemaphoreOption,
+) error {
+ req := conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ updateSemaphore := Ydb_Coordination.SessionRequest_UpdateSemaphore{
+ ReqId: newReqID(),
+ Name: name,
+ }
+ for _, o := range opts {
+ if o != nil {
+ o(&updateSemaphore)
+ }
+ }
+
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_UpdateSemaphore_{
+ UpdateSemaphore: &updateSemaphore,
+ },
+ }
+ },
+ conversation.WithResponseFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetUpdateSemaphoreResult().GetReqId() == request.GetUpdateSemaphore().GetReqId()
+ }),
+ conversation.WithConflictKey(name),
+ conversation.WithIdempotence(true),
+ )
+ if err := s.controller.PushBack(req); err != nil {
+ return err
+ }
+
+ _, err := s.controller.Await(ctx, req)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *session) DeleteSemaphore(
+ ctx context.Context,
+ name string,
+ opts ...options.DeleteSemaphoreOption,
+) error {
+ req := conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ deleteSemaphore := Ydb_Coordination.SessionRequest_DeleteSemaphore{
+ ReqId: newReqID(),
+ Name: name,
+ }
+ for _, o := range opts {
+ if o != nil {
+ o(&deleteSemaphore)
+ }
+ }
+
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_DeleteSemaphore_{
+ DeleteSemaphore: &deleteSemaphore,
+ },
+ }
+ },
+ conversation.WithResponseFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetDeleteSemaphoreResult().GetReqId() == request.GetDeleteSemaphore().GetReqId()
+ }),
+ conversation.WithConflictKey(name),
+ )
+ if err := s.controller.PushBack(req); err != nil {
+ return err
+ }
+
+ _, err := s.controller.Await(ctx, req)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *session) DescribeSemaphore(
+ ctx context.Context,
+ name string,
+ opts ...options.DescribeSemaphoreOption,
+) (*coordination.SemaphoreDescription, error) {
+ req := conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ describeSemaphore := Ydb_Coordination.SessionRequest_DescribeSemaphore{
+ ReqId: newReqID(),
+ Name: name,
+ }
+ for _, o := range opts {
+ if o != nil {
+ o(&describeSemaphore)
+ }
+ }
+
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_DescribeSemaphore_{
+ DescribeSemaphore: &describeSemaphore,
+ },
+ }
+ },
+ conversation.WithResponseFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetDescribeSemaphoreResult().GetReqId() == request.GetDescribeSemaphore().GetReqId()
+ }),
+ conversation.WithConflictKey(name),
+ conversation.WithIdempotence(true),
+ )
+ if err := s.controller.PushBack(req); err != nil {
+ return nil, err
+ }
+
+ resp, err := s.controller.Await(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+
+ return convertSemaphoreDescription(resp.GetDescribeSemaphoreResult().GetSemaphoreDescription()), nil
+}
+
+func convertSemaphoreDescription(
+ desc *Ydb_Coordination.SemaphoreDescription,
+) *coordination.SemaphoreDescription {
+ var result coordination.SemaphoreDescription
+
+ if desc != nil {
+ result.Name = desc.GetName()
+ result.Limit = desc.GetLimit()
+ result.Ephemeral = desc.GetEphemeral()
+ result.Count = desc.GetCount()
+ result.Data = desc.GetData()
+ result.Owners = convertSemaphoreSessions(desc.GetOwners())
+ result.Waiters = convertSemaphoreSessions(desc.GetWaiters())
+ }
+
+ return &result
+}
+
+func convertSemaphoreSessions(
+ sessions []*Ydb_Coordination.SemaphoreSession,
+) []*coordination.SemaphoreSession {
+ if sessions == nil {
+ return nil
+ }
+
+ result := make([]*coordination.SemaphoreSession, len(sessions))
+ for i, s := range sessions {
+ result[i] = convertSemaphoreSession(s)
+ }
+
+ return result
+}
+
+func convertSemaphoreSession(
+ session *Ydb_Coordination.SemaphoreSession,
+) *coordination.SemaphoreSession {
+ var result coordination.SemaphoreSession
+
+ if session != nil {
+ result.SessionID = session.GetSessionId()
+ result.Count = session.GetCount()
+ result.OrderID = session.GetOrderId()
+ result.Data = session.GetData()
+ if session.GetTimeoutMillis() == math.MaxUint64 {
+ result.Timeout = time.Duration(math.MaxInt64)
+ } else {
+ // The service does not allow big timeout values, so the conversion seems to be safe.
+ result.Timeout = time.Duration(session.GetTimeoutMillis()) * time.Millisecond
+ }
+ }
+
+ return &result
+}
+
+func (s *session) AcquireSemaphore(
+ ctx context.Context,
+ name string,
+ count uint64,
+ opts ...options.AcquireSemaphoreOption,
+) (coordination.Lease, error) {
+ req := conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ acquireSemaphore := Ydb_Coordination.SessionRequest_AcquireSemaphore{
+ ReqId: newReqID(),
+ Name: name,
+ Count: count,
+ TimeoutMillis: math.MaxUint64,
+ }
+ for _, o := range opts {
+ if o != nil {
+ o(&acquireSemaphore)
+ }
+ }
+
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_AcquireSemaphore_{
+ AcquireSemaphore: &acquireSemaphore,
+ },
+ }
+ },
+ conversation.WithResponseFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetAcquireSemaphoreResult().GetReqId() == request.GetAcquireSemaphore().GetReqId()
+ }),
+ conversation.WithAcknowledgeFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetAcquireSemaphorePending().GetReqId() == request.GetAcquireSemaphore().GetReqId()
+ }),
+ conversation.WithCancelMessage(
+ func(request *Ydb_Coordination.SessionRequest) *Ydb_Coordination.SessionRequest {
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_ReleaseSemaphore_{
+ ReleaseSemaphore: &Ydb_Coordination.SessionRequest_ReleaseSemaphore{
+ Name: name,
+ ReqId: newReqID(),
+ },
+ },
+ }
+ },
+ func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetReleaseSemaphoreResult().GetReqId() == request.GetReleaseSemaphore().GetReqId()
+ },
+ ),
+ conversation.WithConflictKey(name),
+ conversation.WithIdempotence(true),
+ )
+ if err := s.controller.PushBack(req); err != nil {
+ return nil, err
+ }
+
+ resp, err := s.controller.Await(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+
+ if !resp.GetAcquireSemaphoreResult().GetAcquired() {
+ return nil, coordination.ErrAcquireTimeout
+ }
+
+ ctx, cancel := context.WithCancel(s.ctx)
+
+ return &lease{
+ session: s,
+ name: name,
+ ctx: ctx,
+ cancel: cancel,
+ }, nil
+}
+
+func (l *lease) Context() context.Context {
+ return l.ctx
+}
+
+func (l *lease) Release() error {
+ req := conversation.NewConversation(
+ func() *Ydb_Coordination.SessionRequest {
+ return &Ydb_Coordination.SessionRequest{
+ Request: &Ydb_Coordination.SessionRequest_ReleaseSemaphore_{
+ ReleaseSemaphore: &Ydb_Coordination.SessionRequest_ReleaseSemaphore{
+ ReqId: newReqID(),
+ Name: l.name,
+ },
+ },
+ }
+ },
+ conversation.WithResponseFilter(func(
+ request *Ydb_Coordination.SessionRequest,
+ response *Ydb_Coordination.SessionResponse,
+ ) bool {
+ return response.GetReleaseSemaphoreResult().GetReqId() == request.GetReleaseSemaphore().GetReqId()
+ }),
+ conversation.WithConflictKey(l.name),
+ conversation.WithIdempotence(true),
+ )
+ if err := l.session.controller.PushBack(req); err != nil {
+ return err
+ }
+
+ _, err := l.session.controller.Await(l.session.ctx, req)
+ if err != nil {
+ return err
+ }
+
+ l.cancel()
+
+ return nil
+}
+
+func (l *lease) Session() coordination.Session {
+ return l.session
+}
diff --git a/internal/credentials/access_error.go b/internal/credentials/access_error.go
index 70f801bc3..777bc3d80 100644
--- a/internal/credentials/access_error.go
+++ b/internal/credentials/access_error.go
@@ -94,10 +94,12 @@ func AccessError(msg string, err error, opts ...authErrorOption) error {
buffer.WriteString(msg)
buffer.WriteString(" (")
for i, opt := range opts {
- if i != 0 {
- buffer.WriteString(",")
+ if opt != nil {
+ if i != 0 {
+ buffer.WriteString(",")
+ }
+ opt.applyAuthErrorOption(buffer)
}
- opt.applyAuthErrorOption(buffer)
}
buffer.WriteString("): %w")
diff --git a/internal/credentials/access_token.go b/internal/credentials/access_token.go
index 2d1365827..c6e205f32 100644
--- a/internal/credentials/access_token.go
+++ b/internal/credentials/access_token.go
@@ -32,7 +32,9 @@ func NewAccessTokenCredentials(token string, opts ...AccessTokenCredentialsOptio
sourceInfo: stack.Record(1),
}
for _, opt := range opts {
- opt.ApplyAccessTokenCredentialsOption(c)
+ if opt != nil {
+ opt.ApplyAccessTokenCredentialsOption(c)
+ }
}
return c
diff --git a/internal/credentials/anonymous.go b/internal/credentials/anonymous.go
index 7bd75f4c3..88d937095 100644
--- a/internal/credentials/anonymous.go
+++ b/internal/credentials/anonymous.go
@@ -28,7 +28,9 @@ func NewAnonymousCredentials(opts ...AnonymousCredentialsOption) *Anonymous {
sourceInfo: stack.Record(1),
}
for _, opt := range opts {
- opt.ApplyAnonymousCredentialsOption(c)
+ if opt != nil {
+ opt.ApplyAnonymousCredentialsOption(c)
+ }
}
return c
diff --git a/internal/credentials/static.go b/internal/credentials/static.go
index 00dcce029..7d9437dfb 100644
--- a/internal/credentials/static.go
+++ b/internal/credentials/static.go
@@ -49,7 +49,9 @@ func NewStaticCredentials(user, password, endpoint string, opts ...StaticCredent
sourceInfo: stack.Record(1),
}
for _, opt := range opts {
- opt.ApplyStaticCredentialsOption(c)
+ if opt != nil {
+ opt.ApplyStaticCredentialsOption(c)
+ }
}
return c
diff --git a/internal/decimal/type.go b/internal/decimal/type.go
new file mode 100644
index 000000000..89956a761
--- /dev/null
+++ b/internal/decimal/type.go
@@ -0,0 +1,19 @@
+package decimal
+
+import "math/big"
+
+type Decimal struct {
+ Bytes [16]byte
+ Precision uint32
+ Scale uint32
+}
+
+func (d *Decimal) String() string {
+ v := FromInt128(d.Bytes, d.Precision, d.Scale)
+
+ return Format(v, d.Precision, d.Scale)
+}
+
+func (d *Decimal) BigInt() *big.Int {
+ return FromInt128(d.Bytes, d.Precision, d.Scale)
+}
diff --git a/internal/discovery/config/config.go b/internal/discovery/config/config.go
index 6ea99d21b..782f3b6b2 100644
--- a/internal/discovery/config/config.go
+++ b/internal/discovery/config/config.go
@@ -29,9 +29,9 @@ func New(opts ...Option) *Config {
interval: DefaultInterval,
trace: &trace.Discovery{},
}
- for _, o := range opts {
- if o != nil {
- o(c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
}
}
diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go
index 8cd0953a0..dfda2660c 100644
--- a/internal/discovery/discovery.go
+++ b/internal/discovery/discovery.go
@@ -19,12 +19,12 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
-func New(ctx context.Context, cc grpc.ClientConnInterface, config *config.Config) (*Client, error) {
+func New(ctx context.Context, cc grpc.ClientConnInterface, config *config.Config) *Client {
return &Client{
config: config,
cc: cc,
client: Ydb_Discovery_V1.NewDiscoveryServiceClient(cc),
- }, nil
+ }
}
var _ discovery.Client = &Client{}
@@ -40,7 +40,7 @@ func (c *Client) Discover(ctx context.Context) (endpoints []endpoint.Endpoint, e
var (
onDone = trace.DiscoveryOnDiscover(
c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/discovery.(*Client).Discover"),
c.config.Endpoint(), c.config.Database(),
)
request = Ydb_Discovery.ListEndpointsRequest{
@@ -80,9 +80,9 @@ func (c *Client) Discover(ctx context.Context) (endpoints []endpoint.Endpoint, e
}
location = result.GetSelfLocation()
- endpoints = make([]endpoint.Endpoint, 0, len(result.Endpoints))
- for _, e := range result.Endpoints {
- if e.Ssl == c.config.Secure() {
+ endpoints = make([]endpoint.Endpoint, 0, len(result.GetEndpoints()))
+ for _, e := range result.GetEndpoints() {
+ if e.GetSsl() == c.config.Secure() {
endpoints = append(endpoints, endpoint.New(
net.JoinHostPort(e.GetAddress(), strconv.Itoa(int(e.GetPort()))),
endpoint.WithLocation(e.GetLocation()),
@@ -99,7 +99,9 @@ func (c *Client) Discover(ctx context.Context) (endpoints []endpoint.Endpoint, e
func (c *Client) WhoAmI(ctx context.Context) (whoAmI *discovery.WhoAmI, err error) {
var (
- onDone = trace.DiscoveryOnWhoAmI(c.config.Trace(), &ctx, stack.FunctionID(""))
+ onDone = trace.DiscoveryOnWhoAmI(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/discovery.(*Client).WhoAmI"),
+ )
request = Ydb_Discovery.WhoAmIRequest{}
response *Ydb_Discovery.WhoAmIResponse
whoAmIResultResult Ydb_Discovery.WhoAmIResult
diff --git a/internal/endpoint/endpoint.go b/internal/endpoint/endpoint.go
index cdaba3710..823d721d0 100644
--- a/internal/endpoint/endpoint.go
+++ b/internal/endpoint/endpoint.go
@@ -110,13 +110,10 @@ func (e *endpoint) LastUpdated() time.Time {
func (e *endpoint) Touch(opts ...Option) {
e.mu.Lock()
defer e.mu.Unlock()
- for _, o := range append(
- []Option{
- withLastUpdated(time.Now()),
- },
- opts...,
- ) {
- o(e)
+ for _, opt := range append([]Option{withLastUpdated(time.Now())}, opts...) {
+ if opt != nil {
+ opt(e)
+ }
}
}
@@ -163,9 +160,9 @@ func New(address string, opts ...Option) *endpoint {
address: address,
lastUpdated: time.Now(),
}
- for _, o := range opts {
- if o != nil {
- o(e)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(e)
}
}
diff --git a/internal/grpcwrapper/rawscheme/entry.go b/internal/grpcwrapper/rawscheme/entry.go
index e0ec8f808..f1fb68bde 100644
--- a/internal/grpcwrapper/rawscheme/entry.go
+++ b/internal/grpcwrapper/rawscheme/entry.go
@@ -26,25 +26,25 @@ func (e *Entry) FromProto(proto *Ydb_Scheme.Entry) error {
if proto == nil {
return xerrors.WithStackTrace(errUnexpectedNilForSchemeEntry)
}
- e.Name = proto.Name
- e.Owner = proto.Owner
- e.Type = EntryType(proto.Type)
+ e.Name = proto.GetName()
+ e.Owner = proto.GetOwner()
+ e.Type = EntryType(proto.GetType())
- e.EffectivePermissions = make([]Permissions, len(proto.EffectivePermissions))
- for i := range proto.EffectivePermissions {
- if err := e.EffectivePermissions[i].FromProto(proto.EffectivePermissions[i]); err != nil {
+ e.EffectivePermissions = make([]Permissions, len(proto.GetEffectivePermissions()))
+ for i := range proto.GetEffectivePermissions() {
+ if err := e.EffectivePermissions[i].FromProto(proto.GetEffectivePermissions()[i]); err != nil {
return err
}
}
- e.Permissions = make([]Permissions, len(proto.Permissions))
- for i := range proto.Permissions {
- if err := e.Permissions[i].FromProto(proto.Permissions[i]); err != nil {
+ e.Permissions = make([]Permissions, len(proto.GetPermissions()))
+ for i := range proto.GetPermissions() {
+ if err := e.Permissions[i].FromProto(proto.GetPermissions()[i]); err != nil {
return err
}
}
- e.SizeBytes = proto.SizeBytes
+ e.SizeBytes = proto.GetSizeBytes()
return nil
}
@@ -74,8 +74,8 @@ func (p *Permissions) FromProto(proto *Ydb_Scheme.Permissions) error {
if proto == nil {
return xerrors.WithStackTrace(errUnexpectedNilForSchemePermissions)
}
- p.Subject = proto.Subject
- p.PermissionNames = proto.PermissionNames
+ p.Subject = proto.GetSubject()
+ p.PermissionNames = proto.GetPermissionNames()
return nil
}
diff --git a/internal/grpcwrapper/rawtopic/alter_topic.go b/internal/grpcwrapper/rawtopic/alter_topic.go
index 125311e01..d7db57d74 100644
--- a/internal/grpcwrapper/rawtopic/alter_topic.go
+++ b/internal/grpcwrapper/rawtopic/alter_topic.go
@@ -64,7 +64,7 @@ type AlterTopicResult struct {
}
func (r *AlterTopicResult) FromProto(proto *Ydb_Topic.AlterTopicResponse) error {
- return r.Operation.FromProtoWithStatusCheck(proto.Operation)
+ return r.Operation.FromProtoWithStatusCheck(proto.GetOperation())
}
type AlterConsumer struct {
diff --git a/internal/grpcwrapper/rawtopic/controlplane_types.go b/internal/grpcwrapper/rawtopic/controlplane_types.go
index 3b2070746..6df4322fb 100644
--- a/internal/grpcwrapper/rawtopic/controlplane_types.go
+++ b/internal/grpcwrapper/rawtopic/controlplane_types.go
@@ -25,7 +25,7 @@ func (c *Consumer) MustFromProto(consumer *Ydb_Topic.Consumer) {
c.Important = consumer.GetImportant()
c.Attributes = consumer.GetAttributes()
c.ReadFrom.MustFromProto(consumer.GetReadFrom())
- c.SupportedCodecs.MustFromProto(consumer.SupportedCodecs)
+ c.SupportedCodecs.MustFromProto(consumer.GetSupportedCodecs())
}
func (c *Consumer) ToProto() *Ydb_Topic.Consumer {
@@ -56,8 +56,8 @@ func (s *PartitioningSettings) FromProto(proto *Ydb_Topic.PartitioningSettings)
return xerrors.WithStackTrace(errUnexpectedNilPartitioningSettings)
}
- s.MinActivePartitions = proto.MinActivePartitions
- s.PartitionCountLimit = proto.PartitionCountLimit
+ s.MinActivePartitions = proto.GetMinActivePartitions()
+ s.PartitionCountLimit = proto.GetPartitionCountLimit()
return nil
}
diff --git a/internal/grpcwrapper/rawtopic/create_topic.go b/internal/grpcwrapper/rawtopic/create_topic.go
index f8ecb6980..fd17da84e 100644
--- a/internal/grpcwrapper/rawtopic/create_topic.go
+++ b/internal/grpcwrapper/rawtopic/create_topic.go
@@ -41,7 +41,7 @@ func (req *CreateTopicRequest) ToProto() *Ydb_Topic.CreateTopicRequest {
proto.Attributes = req.Attributes
proto.Consumers = make([]*Ydb_Topic.Consumer, len(req.Consumers))
- for i := range proto.Consumers {
+ for i := range proto.GetConsumers() {
proto.Consumers[i] = req.Consumers[i].ToProto()
}
@@ -55,5 +55,5 @@ type CreateTopicResult struct {
}
func (r *CreateTopicResult) FromProto(proto *Ydb_Topic.CreateTopicResponse) error {
- return r.Operation.FromProtoWithStatusCheck(proto.Operation)
+ return r.Operation.FromProtoWithStatusCheck(proto.GetOperation())
}
diff --git a/internal/grpcwrapper/rawtopic/describe_topic.go b/internal/grpcwrapper/rawtopic/describe_topic.go
index 50662e37f..a1c3d4838 100644
--- a/internal/grpcwrapper/rawtopic/describe_topic.go
+++ b/internal/grpcwrapper/rawtopic/describe_topic.go
@@ -42,7 +42,7 @@ type DescribeTopicResult struct {
}
func (res *DescribeTopicResult) FromProto(protoResponse *Ydb_Topic.DescribeTopicResponse) error {
- if err := res.Operation.FromProtoWithStatusCheck(protoResponse.Operation); err != nil {
+ if err := res.Operation.FromProtoWithStatusCheck(protoResponse.GetOperation()); err != nil {
return err
}
@@ -51,11 +51,11 @@ func (res *DescribeTopicResult) FromProto(protoResponse *Ydb_Topic.DescribeTopic
return xerrors.WithStackTrace(fmt.Errorf("ydb: describe topic result failed on unmarshal grpc result: %w", err))
}
- if err := res.Self.FromProto(protoResult.Self); err != nil {
+ if err := res.Self.FromProto(protoResult.GetSelf()); err != nil {
return err
}
- if err := res.PartitioningSettings.FromProto(protoResult.PartitioningSettings); err != nil {
+ if err := res.PartitioningSettings.FromProto(protoResult.GetPartitioningSettings()); err != nil {
return err
}
@@ -72,17 +72,17 @@ func (res *DescribeTopicResult) FromProto(protoResponse *Ydb_Topic.DescribeTopic
res.SupportedCodecs = append(res.SupportedCodecs, rawtopiccommon.Codec(v))
}
- res.PartitionWriteSpeedBytesPerSecond = protoResult.PartitionWriteSpeedBytesPerSecond
- res.PartitionWriteBurstBytes = protoResult.PartitionWriteBurstBytes
+ res.PartitionWriteSpeedBytesPerSecond = protoResult.GetPartitionWriteSpeedBytesPerSecond()
+ res.PartitionWriteBurstBytes = protoResult.GetPartitionWriteBurstBytes()
- res.Attributes = protoResult.Attributes
+ res.Attributes = protoResult.GetAttributes()
- res.Consumers = make([]Consumer, len(protoResult.Consumers))
+ res.Consumers = make([]Consumer, len(protoResult.GetConsumers()))
for i := range res.Consumers {
- res.Consumers[i].MustFromProto(protoResult.Consumers[i])
+ res.Consumers[i].MustFromProto(protoResult.GetConsumers()[i])
}
- res.MeteringMode = MeteringMode(protoResult.MeteringMode)
+ res.MeteringMode = MeteringMode(protoResult.GetMeteringMode())
return nil
}
diff --git a/internal/grpcwrapper/rawtopic/drop_topic.go b/internal/grpcwrapper/rawtopic/drop_topic.go
index 586284fa8..0db29973a 100644
--- a/internal/grpcwrapper/rawtopic/drop_topic.go
+++ b/internal/grpcwrapper/rawtopic/drop_topic.go
@@ -23,5 +23,5 @@ type DropTopicResult struct {
}
func (r *DropTopicResult) FromProto(proto *Ydb_Topic.DropTopicResponse) error {
- return r.Operation.FromProtoWithStatusCheck(proto.Operation)
+ return r.Operation.FromProtoWithStatusCheck(proto.GetOperation())
}
diff --git a/internal/grpcwrapper/rawtopic/rawtopiccommon/codec.go b/internal/grpcwrapper/rawtopic/rawtopiccommon/codec.go
index 34b87ebf9..166818331 100644
--- a/internal/grpcwrapper/rawtopic/rawtopiccommon/codec.go
+++ b/internal/grpcwrapper/rawtopic/rawtopiccommon/codec.go
@@ -98,7 +98,7 @@ func (c *SupportedCodecs) ToProto() *Ydb_Topic.SupportedCodecs {
func (c *SupportedCodecs) MustFromProto(proto *Ydb_Topic.SupportedCodecs) {
res := make([]Codec, len(proto.GetCodecs()))
for i := range proto.GetCodecs() {
- res[i].MustFromProto(Ydb_Topic.Codec(proto.Codecs[i]))
+ res[i].MustFromProto(Ydb_Topic.Codec(proto.GetCodecs()[i]))
}
*c = res
}
diff --git a/internal/grpcwrapper/rawtopic/rawtopicreader/messages.go b/internal/grpcwrapper/rawtopic/rawtopicreader/messages.go
index b7b32da1c..ad60427fa 100644
--- a/internal/grpcwrapper/rawtopic/rawtopicreader/messages.go
+++ b/internal/grpcwrapper/rawtopic/rawtopicreader/messages.go
@@ -90,11 +90,11 @@ type UpdateTokenRequest struct {
}
type UpdateTokenResponse struct {
+ rawtopiccommon.UpdateTokenResponse
+
serverMessageImpl
rawtopiccommon.ServerMessageMetadata
-
- rawtopiccommon.UpdateTokenResponse
}
//
@@ -160,7 +160,7 @@ type InitResponse struct {
}
func (g *InitResponse) fromProto(p *Ydb_Topic.StreamReadMessage_InitResponse) {
- g.SessionID = p.SessionId
+ g.SessionID = p.GetSessionId()
}
//
@@ -209,52 +209,52 @@ func (r *ReadResponse) fromProto(p *Ydb_Topic.StreamReadMessage_ReadResponse) er
if p == nil {
return xerrors.WithStackTrace(errUnexpectedNilStreamReadMessageReadResponse)
}
- r.BytesSize = int(p.BytesSize)
+ r.BytesSize = int(p.GetBytesSize())
- r.PartitionData = make([]PartitionData, len(p.PartitionData))
- for partitionIndex := range p.PartitionData {
- srcPartition := p.PartitionData[partitionIndex]
+ r.PartitionData = make([]PartitionData, len(p.GetPartitionData()))
+ for partitionIndex := range p.GetPartitionData() {
+ srcPartition := p.GetPartitionData()[partitionIndex]
if srcPartition == nil {
return xerrors.WithStackTrace(errNilPartitionData)
}
dstPartition := &r.PartitionData[partitionIndex]
- dstPartition.PartitionSessionID.FromInt64(srcPartition.PartitionSessionId)
+ dstPartition.PartitionSessionID.FromInt64(srcPartition.GetPartitionSessionId())
- dstPartition.Batches = make([]Batch, len(srcPartition.Batches))
+ dstPartition.Batches = make([]Batch, len(srcPartition.GetBatches()))
- for batchIndex := range srcPartition.Batches {
- srcBatch := srcPartition.Batches[batchIndex]
+ for batchIndex := range srcPartition.GetBatches() {
+ srcBatch := srcPartition.GetBatches()[batchIndex]
if srcBatch == nil {
return xerrors.WithStackTrace(errUnexpectedNilBatchInPartitionData)
}
dstBatch := &dstPartition.Batches[batchIndex]
- dstBatch.ProducerID = srcBatch.ProducerId
- dstBatch.WriteSessionMeta = srcBatch.WriteSessionMeta
- dstBatch.Codec.MustFromProto(Ydb_Topic.Codec(srcBatch.Codec))
+ dstBatch.ProducerID = srcBatch.GetProducerId()
+ dstBatch.WriteSessionMeta = srcBatch.GetWriteSessionMeta()
+ dstBatch.Codec.MustFromProto(Ydb_Topic.Codec(srcBatch.GetCodec()))
- dstBatch.WrittenAt = srcBatch.WrittenAt.AsTime()
+ dstBatch.WrittenAt = srcBatch.GetWrittenAt().AsTime()
- dstBatch.MessageData = make([]MessageData, len(srcBatch.MessageData))
- for messageIndex := range srcBatch.MessageData {
- srcMessage := srcBatch.MessageData[messageIndex]
+ dstBatch.MessageData = make([]MessageData, len(srcBatch.GetMessageData()))
+ for messageIndex := range srcBatch.GetMessageData() {
+ srcMessage := srcBatch.GetMessageData()[messageIndex]
if srcMessage == nil {
return xerrors.WithStackTrace(errUnexpectedMessageNilInPartitionData)
}
dstMessage := &dstBatch.MessageData[messageIndex]
- dstMessage.Offset.FromInt64(srcMessage.Offset)
- dstMessage.SeqNo = srcMessage.SeqNo
- dstMessage.CreatedAt = srcMessage.CreatedAt.AsTime()
- dstMessage.Data = srcMessage.Data
- dstMessage.UncompressedSize = srcMessage.UncompressedSize
- dstMessage.MessageGroupID = srcMessage.MessageGroupId
- if len(srcMessage.MetadataItems) > 0 {
- dstMessage.MetadataItems = make([]rawtopiccommon.MetadataItem, 0, len(srcMessage.MetadataItems))
- for _, protoItem := range srcMessage.MetadataItems {
+ dstMessage.Offset.FromInt64(srcMessage.GetOffset())
+ dstMessage.SeqNo = srcMessage.GetSeqNo()
+ dstMessage.CreatedAt = srcMessage.GetCreatedAt().AsTime()
+ dstMessage.Data = srcMessage.GetData()
+ dstMessage.UncompressedSize = srcMessage.GetUncompressedSize()
+ dstMessage.MessageGroupID = srcMessage.GetMessageGroupId()
+ if len(srcMessage.GetMetadataItems()) > 0 {
+ dstMessage.MetadataItems = make([]rawtopiccommon.MetadataItem, 0, len(srcMessage.GetMetadataItems()))
+ for _, protoItem := range srcMessage.GetMetadataItems() {
dstMessage.MetadataItems = append(dstMessage.MetadataItems, rawtopiccommon.MetadataItem{
- Key: protoItem.Key,
- Value: protoItem.Value[:len(protoItem.Value):len(protoItem.Value)],
+ Key: protoItem.GetKey(),
+ Value: protoItem.GetValue()[:len(protoItem.GetValue()):len(protoItem.GetValue())],
})
}
}
@@ -338,8 +338,8 @@ func (r *OffsetRange) FromProto(p *Ydb_Topic.OffsetsRange) error {
return xerrors.WithStackTrace(errUnexpectedProtobufInOffsets)
}
- r.Start.FromInt64(p.Start)
- r.End.FromInt64(p.End)
+ r.Start.FromInt64(p.GetStart())
+ r.End.FromInt64(p.GetEnd())
return nil
}
@@ -360,16 +360,16 @@ type CommitOffsetResponse struct {
}
func (r *CommitOffsetResponse) fromProto(proto *Ydb_Topic.StreamReadMessage_CommitOffsetResponse) error {
- r.PartitionsCommittedOffsets = make([]PartitionCommittedOffset, len(proto.PartitionsCommittedOffsets))
+ r.PartitionsCommittedOffsets = make([]PartitionCommittedOffset, len(proto.GetPartitionsCommittedOffsets()))
for i := range r.PartitionsCommittedOffsets {
- srcCommitted := proto.PartitionsCommittedOffsets[i]
+ srcCommitted := proto.GetPartitionsCommittedOffsets()[i]
if srcCommitted == nil {
return xerrors.WithStackTrace(errors.New("unexpected nil while parse commit offset response"))
}
dstCommitted := &r.PartitionsCommittedOffsets[i]
- dstCommitted.PartitionSessionID.FromInt64(srcCommitted.PartitionSessionId)
- dstCommitted.CommittedOffset.FromInt64(srcCommitted.CommittedOffset)
+ dstCommitted.PartitionSessionID.FromInt64(srcCommitted.GetPartitionSessionId())
+ dstCommitted.CommittedOffset.FromInt64(srcCommitted.GetCommittedOffset())
}
return nil
@@ -437,16 +437,16 @@ func (r *StartPartitionSessionRequest) fromProto(p *Ydb_Topic.StreamReadMessage_
return xerrors.WithStackTrace(errUnexpectedProtoNilStartPartitionSessionRequest)
}
- if p.PartitionSession == nil {
+ if p.GetPartitionSession() == nil {
return xerrors.WithStackTrace(errUnexpectedNilPartitionSession)
}
- r.PartitionSession.PartitionID = p.PartitionSession.PartitionId
- r.PartitionSession.Path = p.PartitionSession.Path
- r.PartitionSession.PartitionSessionID.FromInt64(p.PartitionSession.PartitionSessionId)
+ r.PartitionSession.PartitionID = p.GetPartitionSession().GetPartitionId()
+ r.PartitionSession.Path = p.GetPartitionSession().GetPath()
+ r.PartitionSession.PartitionSessionID.FromInt64(p.GetPartitionSession().GetPartitionSessionId())
- r.CommittedOffset.FromInt64(p.CommittedOffset)
+ r.CommittedOffset.FromInt64(p.GetCommittedOffset())
- return r.PartitionOffsets.FromProto(p.PartitionOffsets)
+ return r.PartitionOffsets.FromProto(p.GetPartitionOffsets())
}
type PartitionSession struct {
@@ -491,9 +491,9 @@ func (r *StopPartitionSessionRequest) fromProto(proto *Ydb_Topic.StreamReadMessa
if proto == nil {
return xerrors.WithStackTrace(errUnexpectedGrpcNilStopPartitionSessionRequest)
}
- r.PartitionSessionID.FromInt64(proto.PartitionSessionId)
- r.Graceful = proto.Graceful
- r.CommittedOffset.FromInt64(proto.CommittedOffset)
+ r.PartitionSessionID.FromInt64(proto.GetPartitionSessionId())
+ r.Graceful = proto.GetGraceful()
+ r.CommittedOffset.FromInt64(proto.GetCommittedOffset())
return nil
}
diff --git a/internal/grpcwrapper/rawtopic/rawtopicreader/rawtopicreader.go b/internal/grpcwrapper/rawtopic/rawtopicreader/rawtopicreader.go
index 55520e54b..17ccc026e 100644
--- a/internal/grpcwrapper/rawtopic/rawtopicreader/rawtopicreader.go
+++ b/internal/grpcwrapper/rawtopic/rawtopicreader/rawtopicreader.go
@@ -3,6 +3,7 @@ package rawtopicreader
import (
"errors"
"fmt"
+ "io"
"reflect"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Topic"
@@ -30,6 +31,9 @@ func (s StreamReader) CloseSend() error {
func (s StreamReader) Recv() (ServerMessage, error) {
grpcMess, err := s.Stream.Recv()
+ if xerrors.Is(err, io.EOF) {
+ return nil, err
+ }
if err != nil {
if !xerrors.IsErrorFromServer(err) {
err = xerrors.Transport(err)
@@ -46,7 +50,7 @@ func (s StreamReader) Recv() (ServerMessage, error) {
return nil, xerrors.WithStackTrace(fmt.Errorf("ydb: bad status from topic server: %v", meta.Status))
}
- switch m := grpcMess.ServerMessage.(type) {
+ switch m := grpcMess.GetServerMessage().(type) {
case *Ydb_Topic.StreamReadMessage_FromServer_InitResponse:
resp := &InitResponse{}
resp.ServerMessageMetadata = meta
@@ -102,7 +106,7 @@ func (s StreamReader) Recv() (ServerMessage, error) {
default:
return nil, xerrors.WithStackTrace(fmt.Errorf(
"ydb: receive unexpected message (%v): %w",
- reflect.TypeOf(grpcMess.ServerMessage),
+ reflect.TypeOf(grpcMess.GetServerMessage()),
ErrUnexpectedMessageType,
))
}
diff --git a/internal/grpcwrapper/rawtopic/rawtopicwriter/messages.go b/internal/grpcwrapper/rawtopic/rawtopicwriter/messages.go
index 5f89a2f6b..252b4b3cb 100644
--- a/internal/grpcwrapper/rawtopic/rawtopicwriter/messages.go
+++ b/internal/grpcwrapper/rawtopic/rawtopicwriter/messages.go
@@ -133,10 +133,10 @@ type InitResult struct {
}
func (r *InitResult) mustFromProto(response *Ydb_Topic.StreamWriteMessage_InitResponse) {
- r.SessionID = response.SessionId
- r.PartitionID = response.PartitionId
- r.LastSeqNo = response.LastSeqNo
- r.SupportedCodecs.MustFromProto(response.SupportedCodecs)
+ r.SessionID = response.GetSessionId()
+ r.PartitionID = response.GetPartitionId()
+ r.LastSeqNo = response.GetLastSeqNo()
+ r.SupportedCodecs.MustFromProto(response.GetSupportedCodecs())
}
type WriteRequest struct {
@@ -188,7 +188,7 @@ func (d *MessageData) ToProto() (*Ydb_Topic.StreamWriteMessage_WriteRequest_Mess
}
for i := range d.MetadataItems {
- res.MetadataItems = append(res.MetadataItems, &Ydb_Topic.MetadataItem{
+ res.MetadataItems = append(res.GetMetadataItems(), &Ydb_Topic.MetadataItem{
Key: d.MetadataItems[i].Key,
Value: d.MetadataItems[i].Value,
})
@@ -210,15 +210,15 @@ func (r *WriteResult) fromProto(response *Ydb_Topic.StreamWriteMessage_WriteResp
if response == nil {
return xerrors.WithStackTrace(errWriteResultProtoIsNil)
}
- r.Acks = make([]WriteAck, len(response.Acks))
- for i := range response.Acks {
- if err := r.Acks[i].fromProto(response.Acks[i]); err != nil {
+ r.Acks = make([]WriteAck, len(response.GetAcks()))
+ for i := range response.GetAcks() {
+ if err := r.Acks[i].fromProto(response.GetAcks()[i]); err != nil {
return err
}
}
- r.PartitionID = response.PartitionId
+ r.PartitionID = response.GetPartitionId()
- return r.WriteStatistics.fromProto(response.WriteStatistics)
+ return r.WriteStatistics.fromProto(response.GetWriteStatistics())
}
type WriteAck struct {
@@ -230,9 +230,9 @@ func (wa *WriteAck) fromProto(pb *Ydb_Topic.StreamWriteMessage_WriteResponse_Wri
if pb == nil {
return xerrors.WithStackTrace(errWriteResultResponseWriteAckIsNil)
}
- wa.SeqNo = pb.SeqNo
+ wa.SeqNo = pb.GetSeqNo()
- return wa.MessageWriteStatus.fromProto(pb.MessageWriteStatus)
+ return wa.MessageWriteStatus.fromProto(pb.GetMessageWriteStatus())
}
// MessageWriteStatus is struct because it included in per-message structure and
@@ -248,12 +248,12 @@ func (s *MessageWriteStatus) fromProto(status interface{}) error {
switch v := status.(type) {
case *Ydb_Topic.StreamWriteMessage_WriteResponse_WriteAck_Written_:
s.Type = WriteStatusTypeWritten
- s.WrittenOffset = v.Written.Offset
+ s.WrittenOffset = v.Written.GetOffset()
return nil
case *Ydb_Topic.StreamWriteMessage_WriteResponse_WriteAck_Skipped_:
s.Type = WriteStatusTypeSkipped
- s.SkippedReason = WriteStatusSkipReason(v.Skipped.Reason)
+ s.SkippedReason = WriteStatusSkipReason(v.Skipped.GetReason())
return nil
default:
@@ -288,10 +288,10 @@ func (s *WriteStatistics) fromProto(statistics *Ydb_Topic.StreamWriteMessage_Wri
return xerrors.WithStackTrace(errWriteResultResponseStatisticIsNil)
}
- s.PersistingTime = statistics.PersistingTime.AsDuration()
- s.MinQueueWaitTime = statistics.MinQueueWaitTime.AsDuration()
- s.MaxQueueWaitTime = statistics.MaxQueueWaitTime.AsDuration()
- s.TopicQuotaWaitTime = statistics.TopicQuotaWaitTime.AsDuration()
+ s.PersistingTime = statistics.GetPersistingTime().AsDuration()
+ s.MinQueueWaitTime = statistics.GetMinQueueWaitTime().AsDuration()
+ s.MaxQueueWaitTime = statistics.GetMaxQueueWaitTime().AsDuration()
+ s.TopicQuotaWaitTime = statistics.GetTopicQuotaWaitTime().AsDuration()
return nil
}
@@ -303,9 +303,9 @@ type UpdateTokenRequest struct {
}
type UpdateTokenResponse struct {
+ rawtopiccommon.UpdateTokenResponse
+
serverMessageImpl
rawtopiccommon.ServerMessageMetadata
-
- rawtopiccommon.UpdateTokenResponse
}
diff --git a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go
index 8e11a2372..8ff0b9727 100644
--- a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go
+++ b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go
@@ -59,7 +59,7 @@ func (w *StreamWriter) Recv() (ServerMessage, error) {
return nil, xerrors.WithStackTrace(fmt.Errorf("ydb: bad status from topic server: %v", meta.Status))
}
- switch v := grpcMsg.ServerMessage.(type) {
+ switch v := grpcMsg.GetServerMessage().(type) {
case *Ydb_Topic.StreamWriteMessage_FromServer_InitResponse:
var res InitResult
res.ServerMessageMetadata = meta
@@ -172,7 +172,7 @@ func sendWriteRequest(send sendFunc, req *Ydb_Topic.StreamWriteMessage_FromClien
return sendErr
}
- grpcMessages := req.WriteRequest.Messages
+ grpcMessages := req.WriteRequest.GetMessages()
if grpcStatus.Code() != codes.ResourceExhausted || len(grpcMessages) < 2 {
return sendErr
}
diff --git a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go
index e8b1957b1..f076adab9 100644
--- a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go
+++ b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go
@@ -25,7 +25,7 @@ func TestSendWriteRequest(t *testing.T) {
sendCounter := 0
var send sendFunc = func(req *Ydb_Topic.StreamWriteMessage_FromClient) error {
sendCounter++
- require.Equal(t, expected, req.ClientMessage)
+ require.Equal(t, expected, req.GetClientMessage())
return nil
}
@@ -72,7 +72,7 @@ func TestSendWriteRequest(t *testing.T) {
}
getWriteRequest := func(req *Ydb_Topic.StreamWriteMessage_FromClient) *Ydb_Topic.StreamWriteMessage_WriteRequest {
- return req.ClientMessage.(*Ydb_Topic.StreamWriteMessage_FromClient_WriteRequest).WriteRequest
+ return req.GetClientMessage().(*Ydb_Topic.StreamWriteMessage_FromClient_WriteRequest).WriteRequest
}
sendCounter := 0
diff --git a/internal/grpcwrapper/rawydb/operation.go b/internal/grpcwrapper/rawydb/operation.go
index 05635a214..5073cb8aa 100644
--- a/internal/grpcwrapper/rawydb/operation.go
+++ b/internal/grpcwrapper/rawydb/operation.go
@@ -22,7 +22,7 @@ func (o *Operation) FromProto(proto *Ydb_Operations.Operation) error {
return err
}
- return o.Issues.FromProto(proto.Issues)
+ return o.Issues.FromProto(proto.GetIssues())
}
func (o *Operation) OperationStatusToError() error {
diff --git a/internal/meta/context.go b/internal/meta/context.go
index 041f7a5da..f0d0f767b 100644
--- a/internal/meta/context.go
+++ b/internal/meta/context.go
@@ -23,9 +23,15 @@ func traceID(ctx context.Context) (string, bool) {
return "", false
}
-// WithUserAgent returns a copy of parent context with custom user-agent info
-func WithUserAgent(ctx context.Context, userAgent string) context.Context {
- return metadata.AppendToOutgoingContext(ctx, HeaderUserAgent, userAgent)
+// WithApplicationName returns a copy of parent context with custom user-agent info
+func WithApplicationName(ctx context.Context, applicationName string) context.Context {
+ md, has := metadata.FromOutgoingContext(ctx)
+ if !has {
+ md = metadata.MD{}
+ }
+ md.Set(HeaderApplicationName, applicationName)
+
+ return metadata.NewOutgoingContext(ctx, md)
}
// WithRequestType returns a copy of parent context with custom request type
diff --git a/internal/meta/context_test.go b/internal/meta/context_test.go
new file mode 100644
index 000000000..1bfdabcc5
--- /dev/null
+++ b/internal/meta/context_test.go
@@ -0,0 +1,61 @@
+package meta
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc/metadata"
+)
+
+func TestContext(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ ctx context.Context
+ header string
+ values []string
+ }{
+ {
+ name: "WithApplicationName",
+ ctx: WithApplicationName(context.Background(), "test"),
+ header: HeaderApplicationName,
+ values: []string{"test"},
+ },
+ {
+ name: "WithApplicationName",
+ ctx: WithApplicationName(
+ WithApplicationName(
+ context.Background(),
+ "test1",
+ ),
+ "test2",
+ ),
+ header: HeaderApplicationName,
+ values: []string{"test2"},
+ },
+ {
+ name: "WithTraceID",
+ ctx: WithTraceID(context.Background(), "my-trace-id"),
+ header: HeaderTraceID,
+ values: []string{"my-trace-id"},
+ },
+ {
+ name: "WithRequestType",
+ ctx: WithRequestType(context.Background(), "my-request-type"),
+ header: HeaderRequestType,
+ values: []string{"my-request-type"},
+ },
+ {
+ name: "WithAllowFeatures",
+ ctx: WithAllowFeatures(context.Background(), "feature-1", "feature-2", "feature-3"),
+ header: HeaderClientCapabilities,
+ values: []string{"feature-1", "feature-2", "feature-3"},
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ md, has := metadata.FromOutgoingContext(tt.ctx)
+ require.True(t, has)
+ require.Equal(t, tt.values, md.Get(tt.header))
+ })
+ }
+}
diff --git a/internal/meta/headers.go b/internal/meta/headers.go
index e68ca4ad4..41f025941 100644
--- a/internal/meta/headers.go
+++ b/internal/meta/headers.go
@@ -7,8 +7,9 @@ const (
HeaderVersion = "x-ydb-sdk-build-info"
HeaderRequestType = "x-ydb-request-type"
HeaderTraceID = "x-ydb-trace-id"
- HeaderUserAgent = "x-ydb-user-agent"
+ HeaderApplicationName = "x-ydb-application-name"
HeaderClientCapabilities = "x-ydb-client-capabilities"
+ HeaderClientPid = "x-ydb-client-pid"
// outgoing hints
HintSessionBalancer = "session-balancer"
diff --git a/internal/meta/meta.go b/internal/meta/meta.go
index 4f1564732..8f856379d 100644
--- a/internal/meta/meta.go
+++ b/internal/meta/meta.go
@@ -3,6 +3,8 @@ package meta
import (
"context"
"fmt"
+ "os"
+ "strconv"
"google.golang.org/grpc/metadata"
@@ -13,6 +15,8 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
+var pid = os.Getpid()
+
func New(
database string,
credentials credentials.Credentials,
@@ -20,13 +24,14 @@ func New(
opts ...Option,
) *Meta {
m := &Meta{
+ pid: strconv.Itoa(pid),
trace: trace,
credentials: credentials,
database: database,
}
- for _, o := range opts {
- if o != nil {
- o(m)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(m)
}
}
@@ -35,9 +40,9 @@ func New(
type Option func(m *Meta)
-func WithUserAgentOption(userAgent string) Option {
+func WithApplicationNameOption(applicationName string) Option {
return func(m *Meta) {
- m.userAgents = append(m.userAgents, userAgent)
+ m.applicationName = applicationName
}
}
@@ -67,12 +72,13 @@ func ForbidOption(feature string) Option {
}
type Meta struct {
- trace *trace.Driver
- credentials credentials.Credentials
- database string
- requestsType string
- userAgents []string
- capabilities []string
+ pid string
+ trace *trace.Driver
+ credentials credentials.Credentials
+ database string
+ requestsType string
+ applicationName string
+ capabilities []string
}
func (m *Meta) meta(ctx context.Context) (_ metadata.MD, err error) {
@@ -81,6 +87,8 @@ func (m *Meta) meta(ctx context.Context) (_ metadata.MD, err error) {
md = metadata.MD{}
}
+ md.Set(HeaderClientPid, m.pid)
+
if len(md.Get(HeaderDatabase)) == 0 {
md.Set(HeaderDatabase, m.database)
}
@@ -95,8 +103,8 @@ func (m *Meta) meta(ctx context.Context) (_ metadata.MD, err error) {
}
}
- if len(m.userAgents) != 0 {
- md.Append(HeaderUserAgent, m.userAgents...)
+ if m.applicationName != "" {
+ md.Append(HeaderApplicationName, m.applicationName)
}
if len(m.capabilities) > 0 {
@@ -109,7 +117,9 @@ func (m *Meta) meta(ctx context.Context) (_ metadata.MD, err error) {
var token string
- done := trace.DriverOnGetCredentials(m.trace, &ctx, stack.FunctionID(""))
+ done := trace.DriverOnGetCredentials(m.trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/meta.(*Meta).meta"),
+ )
defer func() {
done(token, err)
}()
diff --git a/internal/meta/test/meta_test.go b/internal/meta/test/meta_test.go
index e077b7f6b..139187ef6 100644
--- a/internal/meta/test/meta_test.go
+++ b/internal/meta/test/meta_test.go
@@ -20,13 +20,11 @@ func TestMetaRequiredHeaders(t *testing.T) {
credentials.NewAccessTokenCredentials("token"),
&trace.Driver{},
internal.WithRequestTypeOption("requestType"),
- internal.WithUserAgentOption("user-agent"),
+ internal.WithApplicationNameOption("test app"),
)
ctx := context.Background()
- ctx = meta.WithUserAgent(ctx, "userAgent")
-
ctx = meta.WithTraceID(ctx, "traceID")
ctx = metadata.AppendToOutgoingContext(ctx, "some-user-header", "some-user-value")
@@ -43,7 +41,9 @@ func TestMetaRequiredHeaders(t *testing.T) {
require.Equal(t, []string{"database"}, md.Get(internal.HeaderDatabase))
require.Equal(t, []string{"requestType"}, md.Get(internal.HeaderRequestType))
require.Equal(t, []string{"token"}, md.Get(internal.HeaderTicket))
- require.Equal(t, []string{"userAgent", "user-agent"}, md.Get(internal.HeaderUserAgent))
+ require.NotEmpty(t, md.Get(internal.HeaderClientPid))
+ require.NotEmpty(t, md.Get(internal.HeaderClientPid)[0])
+ require.Equal(t, []string{"test app"}, md.Get(internal.HeaderApplicationName))
require.Equal(t, []string{"traceID"}, md.Get(internal.HeaderTraceID))
require.Equal(t, []string{
"ydb-go-sdk/" + version.Major + "." + version.Minor + "." + version.Patch,
diff --git a/internal/meta/trace_id.go b/internal/meta/trace_id.go
index 926722579..182c57dac 100644
--- a/internal/meta/trace_id.go
+++ b/internal/meta/trace_id.go
@@ -19,7 +19,9 @@ func TraceID(ctx context.Context, opts ...func(opts *newTraceIDOpts)) (context.C
}
options := newTraceIDOpts{newRandom: uuid.NewRandom}
for _, opt := range opts {
- opt(&options)
+ if opt != nil {
+ opt(&options)
+ }
}
uuid, err := options.newRandom()
if err != nil {
diff --git a/internal/operation/context.go b/internal/operation/context.go
index 3ce2e1ee5..2f76340a5 100644
--- a/internal/operation/context.go
+++ b/internal/operation/context.go
@@ -13,7 +13,7 @@ type (
// WithTimeout returns a copy of parent context in which YDB operation timeout
// parameter is set to d. If parent context timeout is smaller than d, parent context is returned.
func WithTimeout(ctx context.Context, operationTimeout time.Duration) context.Context {
- if d, ok := Timeout(ctx); ok && operationTimeout >= d {
+ if d, ok := ctxTimeout(ctx); ok && operationTimeout >= d {
// The current cancelation timeout is already smaller than the new one.
return ctx
}
@@ -25,7 +25,7 @@ func WithTimeout(ctx context.Context, operationTimeout time.Duration) context.Co
// cancel after parameter is set to d. If parent context cancellation timeout is smaller
// than d, parent context is returned.
func WithCancelAfter(ctx context.Context, operationCancelAfter time.Duration) context.Context {
- if d, ok := CancelAfter(ctx); ok && operationCancelAfter >= d {
+ if d, ok := ctxCancelAfter(ctx); ok && operationCancelAfter >= d {
// The current cancelation timeout is already smaller than the new one.
return ctx
}
@@ -33,23 +33,23 @@ func WithCancelAfter(ctx context.Context, operationCancelAfter time.Duration) co
return context.WithValue(ctx, ctxOperationCancelAfterKey{}, operationCancelAfter)
}
-// Timeout returns the timeout within given context after which
+// ctxTimeout returns the timeout within given context after which
// YDB should try to cancel operation and return result regardless of the cancelation.
-func Timeout(ctx context.Context) (d time.Duration, ok bool) {
+func ctxTimeout(ctx context.Context) (d time.Duration, ok bool) {
d, ok = ctx.Value(ctxOperationTimeoutKey{}).(time.Duration)
return
}
-// CancelAfter returns the timeout within given context after which
+// ctxCancelAfter returns the timeout within given context after which
// YDB should try to cancel operation and return result regardless of the cancellation.
-func CancelAfter(ctx context.Context) (d time.Duration, ok bool) {
+func ctxCancelAfter(ctx context.Context) (d time.Duration, ok bool) {
d, ok = ctx.Value(ctxOperationCancelAfterKey{}).(time.Duration)
return
}
-func untilDeadline(ctx context.Context) (time.Duration, bool) {
+func ctxUntilDeadline(ctx context.Context) (time.Duration, bool) {
deadline, ok := ctx.Deadline()
if ok {
return time.Until(deadline), true
diff --git a/internal/operation/params.go b/internal/operation/params.go
index f45e941f8..03d342e9a 100644
--- a/internal/operation/params.go
+++ b/internal/operation/params.go
@@ -13,13 +13,13 @@ func Params(
cancelAfter time.Duration,
mode Mode,
) *Ydb_Operations.OperationParams {
- if d, ok := Timeout(ctx); ok {
+ if d, ok := ctxTimeout(ctx); ok {
timeout = d
}
- if d, ok := CancelAfter(ctx); ok {
+ if d, ok := ctxCancelAfter(ctx); ok {
cancelAfter = d
}
- if d, ok := untilDeadline(ctx); mode == ModeSync && ok && d < timeout {
+ if d, ok := ctxUntilDeadline(ctx); mode == ModeSync && ok && d < timeout {
timeout = d
}
if timeout == 0 && cancelAfter == 0 && mode == 0 {
diff --git a/internal/operation/params_test.go b/internal/operation/params_test.go
index 93ac17c5d..605453279 100644
--- a/internal/operation/params_test.go
+++ b/internal/operation/params_test.go
@@ -313,25 +313,25 @@ func TestParams(t *testing.T) {
return
}
- if !reflect.DeepEqual(got.OperationMode, tt.exp.OperationMode) {
+ if !reflect.DeepEqual(got.GetOperationMode(), tt.exp.GetOperationMode()) {
t.Errorf(
"Params().OperationMode: %v, want: %v",
- got.OperationMode,
- tt.exp.OperationMode,
+ got.GetOperationMode(),
+ tt.exp.GetOperationMode(),
)
}
- if !reflect.DeepEqual(got.CancelAfter, tt.exp.CancelAfter) {
+ if !reflect.DeepEqual(got.GetCancelAfter(), tt.exp.GetCancelAfter()) {
t.Errorf(
"Params().CancelAfter: %v, want: %v",
- got.CancelAfter.AsDuration(),
- tt.exp.CancelAfter.AsDuration(),
+ got.GetCancelAfter().AsDuration(),
+ tt.exp.GetCancelAfter().AsDuration(),
)
}
- if got.OperationTimeout.AsDuration() > tt.exp.OperationTimeout.AsDuration() {
+ if got.GetOperationTimeout().AsDuration() > tt.exp.GetOperationTimeout().AsDuration() {
t.Errorf(
"Params().OperationTimeout: %v, want: <= %v",
- got.OperationTimeout.AsDuration(),
- tt.exp.OperationTimeout.AsDuration(),
+ got.GetOperationTimeout().AsDuration(),
+ tt.exp.GetOperationTimeout().AsDuration(),
)
}
})
diff --git a/internal/params/builder.go b/internal/params/builder.go
new file mode 100644
index 000000000..ce8421fb0
--- /dev/null
+++ b/internal/params/builder.go
@@ -0,0 +1,19 @@
+package params
+
+type (
+ Builder struct {
+ params Parameters
+ }
+)
+
+func (b Builder) Build() *Parameters {
+ return &b.params
+}
+
+func (b Builder) Param(name string) *Parameter {
+ return &Parameter{
+ parent: b,
+ name: name,
+ value: nil,
+ }
+}
diff --git a/internal/params/builder_test.go b/internal/params/builder_test.go
new file mode 100644
index 000000000..da1a7dda7
--- /dev/null
+++ b/internal/params/builder_test.go
@@ -0,0 +1,434 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestBuilder(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ args: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int64",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ args: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int32",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ args: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int16",
+ args: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ args: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Bool",
+ args: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ {
+ method: "Text",
+ args: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ args: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ {
+ method: "Float",
+ args: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Double",
+ args: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Interval",
+ args: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ {
+ method: "Date",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ {
+ method: "JSON",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "YSON",
+ args: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ },
+ },
+ },
+ {
+ method: "UUID",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x")
+
+ result, ok := xtest.CallMethod(item, tc.method, tc.args...)[0].(Builder)
+ require.True(t, ok)
+
+ params := result.Build().ToYDB(a)
+
+ require.Equal(t,
+ xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: tc.expected.Type,
+ Value: tc.expected.Value,
+ },
+ }),
+ xtest.ToJSON(params),
+ )
+ })
+ }
+}
diff --git a/internal/params/dict.go b/internal/params/dict.go
new file mode 100644
index 000000000..d772f659e
--- /dev/null
+++ b/internal/params/dict.go
@@ -0,0 +1,469 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ dict struct {
+ parent Builder
+ name string
+ values []value.DictValueField
+ }
+ dictPair struct {
+ parent *dict
+ keyValue value.Value
+ }
+ dictValue struct {
+ pair *dictPair
+ }
+)
+
+func (d *dict) Add() *dictPair {
+ return &dictPair{
+ parent: d,
+ }
+}
+
+func (d *dict) AddPairs(pairs ...value.DictValueField) *dict {
+ d.values = append(d.values, pairs...)
+
+ return d
+}
+
+func (d *dictPair) Text(v string) *dictValue {
+ d.keyValue = value.TextValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Bytes(v []byte) *dictValue {
+ d.keyValue = value.BytesValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Bool(v bool) *dictValue {
+ d.keyValue = value.BoolValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Uint64(v uint64) *dictValue {
+ d.keyValue = value.Uint64Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Int64(v int64) *dictValue {
+ d.keyValue = value.Int64Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Uint32(v uint32) *dictValue {
+ d.keyValue = value.Uint32Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Int32(v int32) *dictValue {
+ d.keyValue = value.Int32Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Uint16(v uint16) *dictValue {
+ d.keyValue = value.Uint16Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Int16(v int16) *dictValue {
+ d.keyValue = value.Int16Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Uint8(v uint8) *dictValue {
+ d.keyValue = value.Uint8Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Int8(v int8) *dictValue {
+ d.keyValue = value.Int8Value(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Float(v float32) *dictValue {
+ d.keyValue = value.FloatValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Double(v float64) *dictValue {
+ d.keyValue = value.DoubleValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Decimal(v [16]byte, precision, scale uint32) *dictValue {
+ d.keyValue = value.DecimalValue(v, precision, scale)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Timestamp(v time.Time) *dictValue {
+ d.keyValue = value.TimestampValueFromTime(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Date(v time.Time) *dictValue {
+ d.keyValue = value.DateValueFromTime(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Datetime(v time.Time) *dictValue {
+ d.keyValue = value.DatetimeValueFromTime(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) Interval(v time.Duration) *dictValue {
+ d.keyValue = value.IntervalValueFromDuration(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) JSON(v string) *dictValue {
+ d.keyValue = value.JSONValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) JSONDocument(v string) *dictValue {
+ d.keyValue = value.JSONDocumentValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) YSON(v []byte) *dictValue {
+ d.keyValue = value.YSONValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) UUID(v [16]byte) *dictValue {
+ d.keyValue = value.UUIDValue(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) TzDate(v time.Time) *dictValue {
+ d.keyValue = value.TzDateValueFromTime(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) TzTimestamp(v time.Time) *dictValue {
+ d.keyValue = value.TzTimestampValueFromTime(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictPair) TzDatetime(v time.Time) *dictValue {
+ d.keyValue = value.TzDatetimeValueFromTime(v)
+
+ return &dictValue{
+ pair: d,
+ }
+}
+
+func (d *dictValue) Text(v string) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.TextValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Bytes(v []byte) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.BytesValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Bool(v bool) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.BoolValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Uint64(v uint64) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Uint64Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Int64(v int64) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Int64Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Uint32(v uint32) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Uint32Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Int32(v int32) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Int32Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Uint16(v uint16) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Uint16Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Int16(v int16) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Int16Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Uint8(v uint8) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Uint8Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Int8(v int8) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.Int8Value(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Float(v float32) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.FloatValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Double(v float64) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.DoubleValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Decimal(v [16]byte, precision, scale uint32) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.DecimalValue(v, precision, scale),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Timestamp(v time.Time) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.TimestampValueFromTime(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Date(v time.Time) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.DateValueFromTime(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Datetime(v time.Time) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.DatetimeValueFromTime(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) Interval(v time.Duration) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.IntervalValueFromDuration(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) JSON(v string) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.JSONValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) JSONDocument(v string) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.JSONDocumentValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) YSON(v []byte) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.YSONValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) UUID(v [16]byte) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.UUIDValue(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) TzDate(v time.Time) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.TzDateValueFromTime(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) TzTimestamp(v time.Time) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.TzTimestampValueFromTime(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dictValue) TzDatetime(v time.Time) *dict {
+ d.pair.parent.values = append(d.pair.parent.values, value.DictValueField{
+ K: d.pair.keyValue,
+ V: value.TzDatetimeValueFromTime(v),
+ })
+
+ return d.pair.parent
+}
+
+func (d *dict) EndDict() Builder {
+ d.parent.params = append(d.parent.params, &Parameter{
+ parent: d.parent,
+ name: d.name,
+ value: value.DictValue(d.values...),
+ })
+
+ return d.parent
+}
diff --git a/internal/params/dict_test.go b/internal/params/dict_test.go
new file mode 100644
index 000000000..0dc6b246c
--- /dev/null
+++ b/internal/params/dict_test.go
@@ -0,0 +1,519 @@
+package params
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestDict(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ args: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int64",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ args: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int32",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ args: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int16",
+ args: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ args: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Bool",
+ args: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ {
+ method: "Text",
+ args: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ args: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ {
+ method: "Float",
+ args: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Double",
+ args: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Interval",
+ args: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ {
+ method: "Date",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ {
+ method: "JSON",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "YSON",
+ args: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ },
+ },
+ },
+ {
+ method: "UUID",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ }
+
+ for _, key := range tests {
+ for _, val := range tests {
+ t.Run(fmt.Sprintf("%s:%s", key.method, val.method), func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginDict().Add()
+
+ addedKey, ok := xtest.CallMethod(item, key.method, key.args...)[0].(*dictValue)
+ require.True(t, ok)
+
+ d, ok := xtest.CallMethod(addedKey, val.method, val.args...)[0].(*dict)
+ require.True(t, ok)
+
+ params := d.EndDict().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DictType{
+ DictType: &Ydb.DictType{
+ Key: key.expected.Type,
+ Payload: val.expected.Type,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Pairs: []*Ydb.ValuePair{
+ {
+ Key: key.expected.Value,
+ Payload: val.expected.Value,
+ },
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+ }
+}
+
+func TestDict_AddPairs(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ pairs := []value.DictValueField{
+ {
+ K: value.Int64Value(123),
+ V: value.BoolValue(true),
+ },
+ {
+ K: value.Int64Value(321),
+ V: value.BoolValue(false),
+ },
+ }
+
+ params := Builder{}.Param("$x").BeginDict().AddPairs(pairs...).EndDict().Build().ToYDB(a)
+
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DictType{
+ DictType: &Ydb.DictType{
+ Key: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ Payload: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Pairs: []*Ydb.ValuePair{
+ {
+ Key: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ Payload: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ {
+ Key: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 321,
+ },
+ },
+ Payload: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ },
+ },
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+}
diff --git a/internal/params/list.go b/internal/params/list.go
new file mode 100644
index 000000000..b70501e5c
--- /dev/null
+++ b/internal/params/list.go
@@ -0,0 +1,190 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ list struct {
+ parent Builder
+ name string
+ values []value.Value
+ }
+ listItem struct {
+ parent *list
+ }
+)
+
+func (l *list) Add() *listItem {
+ return &listItem{
+ parent: l,
+ }
+}
+
+func (l *list) AddItems(items ...value.Value) *list {
+ l.values = append(l.values, items...)
+
+ return l
+}
+
+func (l *list) EndList() Builder {
+ l.parent.params = append(l.parent.params, &Parameter{
+ parent: l.parent,
+ name: l.name,
+ value: value.ListValue(l.values...),
+ })
+
+ return l.parent
+}
+
+func (l *listItem) Text(v string) *list {
+ l.parent.values = append(l.parent.values, value.TextValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) Bytes(v []byte) *list {
+ l.parent.values = append(l.parent.values, value.BytesValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) Bool(v bool) *list {
+ l.parent.values = append(l.parent.values, value.BoolValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) Uint64(v uint64) *list {
+ l.parent.values = append(l.parent.values, value.Uint64Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Int64(v int64) *list {
+ l.parent.values = append(l.parent.values, value.Int64Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Uint32(v uint32) *list {
+ l.parent.values = append(l.parent.values, value.Uint32Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Int32(v int32) *list {
+ l.parent.values = append(l.parent.values, value.Int32Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Uint16(v uint16) *list {
+ l.parent.values = append(l.parent.values, value.Uint16Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Int16(v int16) *list {
+ l.parent.values = append(l.parent.values, value.Int16Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Uint8(v uint8) *list {
+ l.parent.values = append(l.parent.values, value.Uint8Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Int8(v int8) *list {
+ l.parent.values = append(l.parent.values, value.Int8Value(v))
+
+ return l.parent
+}
+
+func (l *listItem) Float(v float32) *list {
+ l.parent.values = append(l.parent.values, value.FloatValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) Double(v float64) *list {
+ l.parent.values = append(l.parent.values, value.DoubleValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) Decimal(v [16]byte, precision, scale uint32) *list {
+ l.parent.values = append(l.parent.values, value.DecimalValue(v, precision, scale))
+
+ return l.parent
+}
+
+func (l *listItem) Timestamp(v time.Time) *list {
+ l.parent.values = append(l.parent.values, value.TimestampValueFromTime(v))
+
+ return l.parent
+}
+
+func (l *listItem) Date(v time.Time) *list {
+ l.parent.values = append(l.parent.values, value.DateValueFromTime(v))
+
+ return l.parent
+}
+
+func (l *listItem) Datetime(v time.Time) *list {
+ l.parent.values = append(l.parent.values, value.DatetimeValueFromTime(v))
+
+ return l.parent
+}
+
+func (l *listItem) Interval(v time.Duration) *list {
+ l.parent.values = append(l.parent.values, value.IntervalValueFromDuration(v))
+
+ return l.parent
+}
+
+func (l *listItem) JSON(v string) *list {
+ l.parent.values = append(l.parent.values, value.JSONValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) JSONDocument(v string) *list {
+ l.parent.values = append(l.parent.values, value.JSONDocumentValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) YSON(v []byte) *list {
+ l.parent.values = append(l.parent.values, value.YSONValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) UUID(v [16]byte) *list {
+ l.parent.values = append(l.parent.values, value.UUIDValue(v))
+
+ return l.parent
+}
+
+func (l *listItem) TzDate(v time.Time) *list {
+ l.parent.values = append(l.parent.values, value.TzDateValueFromTime(v))
+
+ return l.parent
+}
+
+func (l *listItem) TzTimestamp(v time.Time) *list {
+ l.parent.values = append(l.parent.values, value.TzTimestampValueFromTime(v))
+
+ return l.parent
+}
+
+func (l *listItem) TzDatetime(v time.Time) *list {
+ l.parent.values = append(l.parent.values, value.TzDatetimeValueFromTime(v))
+
+ return l.parent
+}
diff --git a/internal/params/list_test.go b/internal/params/list_test.go
new file mode 100644
index 000000000..288e0a5f7
--- /dev/null
+++ b/internal/params/list_test.go
@@ -0,0 +1,475 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestList(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ args: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int64",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ args: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int32",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ args: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int16",
+ args: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ args: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Bool",
+ args: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ {
+ method: "Text",
+ args: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ args: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ {
+ method: "Float",
+ args: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Double",
+ args: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Interval",
+ args: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ {
+ method: "Date",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ {
+ method: "JSON",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "YSON",
+ args: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ },
+ },
+ },
+ {
+ method: "UUID",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginList().Add()
+
+ result, ok := xtest.CallMethod(item, tc.method, tc.args...)[0].(*list)
+ require.True(t, ok)
+
+ params := result.EndList().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_ListType{
+ ListType: &Ydb.ListType{
+ Item: tc.expected.Type,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ tc.expected.Value,
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
+
+func TestList_AddItems(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+ params := Builder{}.Param("$x").BeginList().
+ AddItems(value.Uint64Value(123), value.Uint64Value(321)).
+ EndList().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_ListType{
+ ListType: &Ydb.ListType{
+ Item: &Ydb.Type{Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64}},
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 321,
+ },
+ },
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+}
diff --git a/internal/params/optional.go b/internal/params/optional.go
new file mode 100644
index 000000000..f5aa02efb
--- /dev/null
+++ b/internal/params/optional.go
@@ -0,0 +1,178 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ optional struct {
+ parent Builder
+ name string
+ value value.Value
+ }
+ optionalBuilder struct {
+ opt *optional
+ }
+)
+
+func (b *optionalBuilder) EndOptional() Builder {
+ b.opt.parent.params = append(b.opt.parent.params, &Parameter{
+ parent: b.opt.parent,
+ name: b.opt.name,
+ value: value.OptionalValue(b.opt.value),
+ })
+
+ return b.opt.parent
+}
+
+func (p *optional) Text(v string) *optionalBuilder {
+ p.value = value.TextValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Bytes(v []byte) *optionalBuilder {
+ p.value = value.BytesValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Bool(v bool) *optionalBuilder {
+ p.value = value.BoolValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Uint64(v uint64) *optionalBuilder {
+ p.value = value.Uint64Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Int64(v int64) *optionalBuilder {
+ p.value = value.Int64Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Uint32(v uint32) *optionalBuilder {
+ p.value = value.Uint32Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Int32(v int32) *optionalBuilder {
+ p.value = value.Int32Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Uint16(v uint16) *optionalBuilder {
+ p.value = value.Uint16Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Int16(v int16) *optionalBuilder {
+ p.value = value.Int16Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Uint8(v uint8) *optionalBuilder {
+ p.value = value.Uint8Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Int8(v int8) *optionalBuilder {
+ p.value = value.Int8Value(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Float(v float32) *optionalBuilder {
+ p.value = value.FloatValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Double(v float64) *optionalBuilder {
+ p.value = value.DoubleValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Decimal(v [16]byte, precision, scale uint32) *optionalBuilder {
+ p.value = value.DecimalValue(v, precision, scale)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Timestamp(v time.Time) *optionalBuilder {
+ p.value = value.TimestampValueFromTime(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Date(v time.Time) *optionalBuilder {
+ p.value = value.DateValueFromTime(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Datetime(v time.Time) *optionalBuilder {
+ p.value = value.DatetimeValueFromTime(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) Interval(v time.Duration) *optionalBuilder {
+ p.value = value.IntervalValueFromDuration(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) JSON(v string) *optionalBuilder {
+ p.value = value.JSONValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) JSONDocument(v string) *optionalBuilder {
+ p.value = value.JSONDocumentValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) YSON(v []byte) *optionalBuilder {
+ p.value = value.YSONValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) UUID(v [16]byte) *optionalBuilder {
+ p.value = value.UUIDValue(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) TzDate(v time.Time) *optionalBuilder {
+ p.value = value.TzDateValueFromTime(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) TzTimestamp(v time.Time) *optionalBuilder {
+ p.value = value.TzTimestampValueFromTime(v)
+
+ return &optionalBuilder{opt: p}
+}
+
+func (p *optional) TzDatetime(v time.Time) *optionalBuilder {
+ p.value = value.TzDatetimeValueFromTime(v)
+
+ return &optionalBuilder{opt: p}
+}
diff --git a/internal/params/optional_test.go b/internal/params/optional_test.go
new file mode 100644
index 000000000..0c21718a1
--- /dev/null
+++ b/internal/params/optional_test.go
@@ -0,0 +1,436 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestOptional(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ args: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int64",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ args: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int32",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ args: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int16",
+ args: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ args: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Bool",
+ args: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ {
+ method: "Text",
+ args: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ args: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ {
+ method: "Float",
+ args: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Double",
+ args: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Interval",
+ args: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ {
+ method: "Date",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ {
+ method: "JSON",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "YSON",
+ args: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ },
+ },
+ },
+ {
+ method: "UUID",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginOptional()
+
+ result, ok := xtest.CallMethod(item, tc.method, tc.args...)[0].(*optionalBuilder)
+ require.True(t, ok)
+
+ params := result.EndOptional().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_OptionalType{
+ OptionalType: &Ydb.OptionalType{
+ Item: tc.expected.Type,
+ },
+ },
+ },
+ Value: tc.expected.Value,
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
diff --git a/internal/params/parameters.go b/internal/params/parameters.go
new file mode 100644
index 000000000..aae4e38d0
--- /dev/null
+++ b/internal/params/parameters.go
@@ -0,0 +1,332 @@
+package params
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
+)
+
+type (
+ NamedValue interface {
+ Name() string
+ Value() value.Value
+ }
+ Parameter struct {
+ parent Builder
+ name string
+ value value.Value
+ }
+ Parameters []*Parameter
+)
+
+func Named(name string, value value.Value) *Parameter {
+ return &Parameter{
+ name: name,
+ value: value,
+ }
+}
+
+func (p *Parameter) Name() string {
+ return p.name
+}
+
+func (p *Parameter) Value() value.Value {
+ return p.value
+}
+
+func (p *Parameters) String() string {
+ buffer := xstring.Buffer()
+ defer buffer.Free()
+
+ buffer.WriteByte('{')
+ if p != nil {
+ for i, param := range *p {
+ if i != 0 {
+ buffer.WriteByte(',')
+ }
+ buffer.WriteByte('"')
+ buffer.WriteString(param.name)
+ buffer.WriteString("\":")
+ buffer.WriteString(param.value.Yql())
+ }
+ }
+ buffer.WriteByte('}')
+
+ return buffer.String()
+}
+
+func (p *Parameters) ToYDB(a *allocator.Allocator) map[string]*Ydb.TypedValue {
+ if p == nil {
+ return nil
+ }
+ parameters := make(map[string]*Ydb.TypedValue, len(*p))
+ for _, param := range *p {
+ parameters[param.name] = value.ToYDB(param.value, a)
+ }
+
+ return parameters
+}
+
+func (p *Parameters) Each(it func(name string, v value.Value)) {
+ if p == nil {
+ return
+ }
+ for _, p := range *p {
+ it(p.name, p.value)
+ }
+}
+
+func (p *Parameters) Count() int {
+ if p == nil {
+ return 0
+ }
+
+ return len(*p)
+}
+
+func (p *Parameters) Add(params ...NamedValue) {
+ for _, param := range params {
+ *p = append(*p, Named(param.Name(), param.Value()))
+ }
+}
+
+func (p *Parameter) BeginOptional() *optional {
+ return &optional{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) BeginList() *list {
+ return &list{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) Pg() pgParam {
+ return pgParam{p}
+}
+
+func (p *Parameter) BeginSet() *set {
+ return &set{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) BeginDict() *dict {
+ return &dict{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) BeginTuple() *tuple {
+ return &tuple{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) BeginStruct() *structure {
+ return &structure{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) BeginVariant() *variant {
+ return &variant{
+ parent: p.parent,
+ name: p.name,
+ }
+}
+
+func (p *Parameter) Text(v string) Builder {
+ p.value = value.TextValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Bytes(v []byte) Builder {
+ p.value = value.BytesValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Bool(v bool) Builder {
+ p.value = value.BoolValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Uint64(v uint64) Builder {
+ p.value = value.Uint64Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Int64(v int64) Builder {
+ p.value = value.Int64Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Uint32(v uint32) Builder {
+ p.value = value.Uint32Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Int32(v int32) Builder {
+ p.value = value.Int32Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Uint16(v uint16) Builder {
+ p.value = value.Uint16Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Int16(v int16) Builder {
+ p.value = value.Int16Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Uint8(v uint8) Builder {
+ p.value = value.Uint8Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Int8(v int8) Builder {
+ p.value = value.Int8Value(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Float(v float32) Builder {
+ p.value = value.FloatValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Double(v float64) Builder {
+ p.value = value.DoubleValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Decimal(v [16]byte, precision, scale uint32) Builder {
+ p.value = value.DecimalValue(v, precision, scale)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Timestamp(v time.Time) Builder {
+ p.value = value.TimestampValueFromTime(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Date(v time.Time) Builder {
+ p.value = value.DateValueFromTime(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Datetime(v time.Time) Builder {
+ p.value = value.DatetimeValueFromTime(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) Interval(v time.Duration) Builder {
+ p.value = value.IntervalValueFromDuration(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) JSON(v string) Builder {
+ p.value = value.JSONValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) JSONDocument(v string) Builder {
+ p.value = value.JSONDocumentValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) YSON(v []byte) Builder {
+ p.value = value.YSONValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) UUID(v [16]byte) Builder {
+ p.value = value.UUIDValue(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) TzDate(v time.Time) Builder {
+ p.value = value.TzDateValueFromTime(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) TzTimestamp(v time.Time) Builder {
+ p.value = value.TzTimestampValueFromTime(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func (p *Parameter) TzDatetime(v time.Time) Builder {
+ p.value = value.TzDatetimeValueFromTime(v)
+ p.parent.params = append(p.parent.params, p)
+
+ return p.parent
+}
+
+func Declare(p *Parameter) string {
+ return fmt.Sprintf(
+ "DECLARE %s AS %s",
+ p.name,
+ p.value.Type().Yql(),
+ )
+}
diff --git a/internal/params/parameters_test.go b/internal/params/parameters_test.go
new file mode 100644
index 000000000..1c49c3f45
--- /dev/null
+++ b/internal/params/parameters_test.go
@@ -0,0 +1,70 @@
+package params
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestParameter(t *testing.T) {
+ p := Named("x", value.TextValue("X"))
+ require.Equal(t, "x", p.Name())
+ require.EqualValues(t, "X", p.Value())
+ require.Equal(t, "DECLARE x AS Utf8", Declare(p))
+}
+
+func TestParameters(t *testing.T) {
+ p := &Parameters{}
+ p.Add(
+ Named("x", value.TextValue("X")),
+ Named("y", value.TextValue("Y")),
+ )
+ require.Equal(t, "{\"x\":\"X\"u,\"y\":\"Y\"u}", p.String())
+ require.Equal(t, 2, p.Count())
+ visited := make(map[string]value.Value, 2)
+ p.Each(func(name string, v value.Value) {
+ visited[name] = v
+ })
+ require.Len(t, visited, 2)
+ require.EqualValues(t, map[string]value.Value{
+ "x": value.TextValue("X"),
+ "y": value.TextValue("Y"),
+ }, visited)
+}
+
+func TestNil(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ p *Parameters
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ p: nil,
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ p: &Parameters{},
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ p: Builder{}.Build(),
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ require.Equal(t, "{}", tt.p.String())
+ require.Equal(t, 0, tt.p.Count())
+ visited := make(map[string]value.Value, 1)
+ tt.p.Each(func(name string, v value.Value) {
+ visited[name] = v
+ })
+ require.Empty(t, visited)
+ a := allocator.New()
+ defer a.Free()
+ require.Empty(t, tt.p.ToYDB(a))
+ })
+ }
+}
diff --git a/internal/params/pg.go b/internal/params/pg.go
new file mode 100644
index 000000000..542957859
--- /dev/null
+++ b/internal/params/pg.go
@@ -0,0 +1,31 @@
+package params
+
+import (
+ "strconv"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pg"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type pgParam struct {
+ param *Parameter
+}
+
+func (p pgParam) Unknown(val string) Builder {
+ return p.Value(pg.OIDUnknown, val)
+}
+
+func (p pgParam) Value(oid uint32, val string) Builder {
+ p.param.value = value.PgValue(oid, val)
+ p.param.parent.params = append(p.param.parent.params, p.param)
+
+ return p.param.parent
+}
+
+func (p pgParam) Int4(val int32) Builder {
+ return p.Value(pg.OIDInt4, strconv.FormatInt(int64(val), 10))
+}
+
+func (p pgParam) Int8(val int64) Builder {
+ return p.Value(pg.OIDInt8, strconv.FormatInt(val, 10))
+}
diff --git a/internal/params/pg_test.go b/internal/params/pg_test.go
new file mode 100644
index 000000000..ce470864c
--- /dev/null
+++ b/internal/params/pg_test.go
@@ -0,0 +1,100 @@
+package params
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pg"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestPg(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Unknown",
+ args: []any{"123"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_PgType{
+ PgType: &Ydb.PgType{
+ Oid: pg.OIDUnknown,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{TextValue: "123"},
+ },
+ },
+ },
+ {
+ method: "Int4",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_PgType{
+ PgType: &Ydb.PgType{
+ Oid: pg.OIDInt4,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{TextValue: "123"},
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_PgType{
+ PgType: &Ydb.PgType{
+ Oid: pg.OIDInt8,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{TextValue: "123"},
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").Pg()
+
+ result, ok := xtest.CallMethod(item, tc.method, tc.args...)[0].(Builder)
+ require.True(t, ok)
+
+ params := result.Build().ToYDB(a)
+
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: tc.expected.Type,
+ Value: tc.expected.Value,
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
diff --git a/internal/params/set.go b/internal/params/set.go
new file mode 100644
index 000000000..cb2303fde
--- /dev/null
+++ b/internal/params/set.go
@@ -0,0 +1,191 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ set struct {
+ parent Builder
+ name string
+ values []value.Value
+ }
+
+ setItem struct {
+ parent *set
+ }
+)
+
+func (s *set) Add() *setItem {
+ return &setItem{
+ parent: s,
+ }
+}
+
+func (s *set) AddItems(items ...value.Value) *set {
+ s.values = append(s.values, items...)
+
+ return s
+}
+
+func (s *set) EndSet() Builder {
+ s.parent.params = append(s.parent.params, &Parameter{
+ parent: s.parent,
+ name: s.name,
+ value: value.SetValue(s.values...),
+ })
+
+ return s.parent
+}
+
+func (s *setItem) Text(v string) *set {
+ s.parent.values = append(s.parent.values, value.TextValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) Bytes(v []byte) *set {
+ s.parent.values = append(s.parent.values, value.BytesValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) Bool(v bool) *set {
+ s.parent.values = append(s.parent.values, value.BoolValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) Uint64(v uint64) *set {
+ s.parent.values = append(s.parent.values, value.Uint64Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Int64(v int64) *set {
+ s.parent.values = append(s.parent.values, value.Int64Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Uint32(v uint32) *set {
+ s.parent.values = append(s.parent.values, value.Uint32Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Int32(v int32) *set {
+ s.parent.values = append(s.parent.values, value.Int32Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Uint16(v uint16) *set {
+ s.parent.values = append(s.parent.values, value.Uint16Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Int16(v int16) *set {
+ s.parent.values = append(s.parent.values, value.Int16Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Uint8(v uint8) *set {
+ s.parent.values = append(s.parent.values, value.Uint8Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Int8(v int8) *set {
+ s.parent.values = append(s.parent.values, value.Int8Value(v))
+
+ return s.parent
+}
+
+func (s *setItem) Float(v float32) *set {
+ s.parent.values = append(s.parent.values, value.FloatValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) Double(v float64) *set {
+ s.parent.values = append(s.parent.values, value.DoubleValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) Decimal(v [16]byte, precision, scale uint32) *set {
+ s.parent.values = append(s.parent.values, value.DecimalValue(v, precision, scale))
+
+ return s.parent
+}
+
+func (s *setItem) Timestamp(v time.Time) *set {
+ s.parent.values = append(s.parent.values, value.TimestampValueFromTime(v))
+
+ return s.parent
+}
+
+func (s *setItem) Date(v time.Time) *set {
+ s.parent.values = append(s.parent.values, value.DateValueFromTime(v))
+
+ return s.parent
+}
+
+func (s *setItem) Datetime(v time.Time) *set {
+ s.parent.values = append(s.parent.values, value.DatetimeValueFromTime(v))
+
+ return s.parent
+}
+
+func (s *setItem) Interval(v time.Duration) *set {
+ s.parent.values = append(s.parent.values, value.IntervalValueFromDuration(v))
+
+ return s.parent
+}
+
+func (s *setItem) JSON(v string) *set {
+ s.parent.values = append(s.parent.values, value.JSONValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) JSONDocument(v string) *set {
+ s.parent.values = append(s.parent.values, value.JSONDocumentValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) YSON(v []byte) *set {
+ s.parent.values = append(s.parent.values, value.YSONValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) UUID(v [16]byte) *set {
+ s.parent.values = append(s.parent.values, value.UUIDValue(v))
+
+ return s.parent
+}
+
+func (s *setItem) TzDate(v time.Time) *set {
+ s.parent.values = append(s.parent.values, value.TzDateValueFromTime(v))
+
+ return s.parent
+}
+
+func (s *setItem) TzTimestamp(v time.Time) *set {
+ s.parent.values = append(s.parent.values, value.TzTimestampValueFromTime(v))
+
+ return s.parent
+}
+
+func (s *setItem) TzDatetime(v time.Time) *set {
+ s.parent.values = append(s.parent.values, value.TzDatetimeValueFromTime(v))
+
+ return s.parent
+}
diff --git a/internal/params/set_test.go b/internal/params/set_test.go
new file mode 100644
index 000000000..6ff75d464
--- /dev/null
+++ b/internal/params/set_test.go
@@ -0,0 +1,500 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestSet(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ args: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int64",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ args: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int32",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ args: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int16",
+ args: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ args: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Bool",
+ args: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ {
+ method: "Text",
+ args: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ args: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ {
+ method: "Float",
+ args: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Double",
+ args: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Interval",
+ args: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ {
+ method: "Date",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ {
+ method: "JSON",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "YSON",
+ args: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ },
+ },
+ },
+ {
+ method: "UUID",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginSet().Add()
+
+ result, ok := xtest.CallMethod(item, tc.method, tc.args...)[0].(*set)
+ require.True(t, ok)
+
+ params := result.EndSet().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DictType{
+ DictType: &Ydb.DictType{
+ Key: tc.expected.Type,
+ Payload: &Ydb.Type{
+ Type: &Ydb.Type_VoidType{},
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Pairs: []*Ydb.ValuePair{
+ {
+ Key: tc.expected.Value,
+ Payload: &Ydb.Value{
+ Value: &Ydb.Value_NullFlagValue{},
+ },
+ },
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
+
+func TestSet_AddItems(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+ params := Builder{}.Param("$x").BeginSet().
+ AddItems(value.Uint64Value(123), value.Uint64Value(321)).
+ EndSet().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DictType{
+ DictType: &Ydb.DictType{
+ Key: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ Payload: &Ydb.Type{
+ Type: &Ydb.Type_VoidType{},
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Pairs: []*Ydb.ValuePair{
+ {
+ Key: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ Payload: &Ydb.Value{
+ Value: &Ydb.Value_NullFlagValue{},
+ },
+ },
+ {
+ Key: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 321,
+ },
+ },
+ Payload: &Ydb.Value{
+ Value: &Ydb.Value_NullFlagValue{},
+ },
+ },
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+}
diff --git a/internal/params/struct.go b/internal/params/struct.go
new file mode 100644
index 000000000..0a8af589b
--- /dev/null
+++ b/internal/params/struct.go
@@ -0,0 +1,268 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ structure struct {
+ parent Builder
+ name string
+ values []value.StructValueField
+ }
+
+ structValue struct {
+ parent *structure
+ name string
+ }
+)
+
+func (s *structure) AddItems(items ...value.StructValueField) *structure {
+ s.values = append(s.values, items...)
+
+ return s
+}
+
+func (s *structure) Field(v string) *structValue {
+ return &structValue{
+ parent: s,
+ name: v,
+ }
+}
+
+func (s *structure) EndStruct() Builder {
+ s.parent.params = append(s.parent.params, &Parameter{
+ parent: s.parent,
+ name: s.name,
+ value: value.StructValue(s.values...),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Text(v string) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.TextValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Bytes(v []byte) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.BytesValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Bool(v bool) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.BoolValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Uint64(v uint64) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Uint64Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Int64(v int64) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Int64Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Uint32(v uint32) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Uint32Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Int32(v int32) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Int32Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Uint16(v uint16) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Uint16Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Int16(v int16) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Int16Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Uint8(v uint8) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Uint8Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Int8(v int8) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.Int8Value(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Float(v float32) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.FloatValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Double(v float64) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.DoubleValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Decimal(v [16]byte, precision, scale uint32) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.DecimalValue(v, precision, scale),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Timestamp(v time.Time) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.TimestampValueFromTime(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Date(v time.Time) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.DateValueFromTime(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Datetime(v time.Time) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.DatetimeValueFromTime(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) Interval(v time.Duration) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.IntervalValueFromDuration(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) JSON(v string) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.JSONValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) JSONDocument(v string) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.JSONDocumentValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) YSON(v []byte) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.YSONValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) UUID(v [16]byte) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.UUIDValue(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) TzDatetime(v time.Time) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.TzDatetimeValueFromTime(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) TzTimestamp(v time.Time) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.TzTimestampValueFromTime(v),
+ })
+
+ return s.parent
+}
+
+func (s *structValue) TzDate(v time.Time) *structure {
+ s.parent.values = append(s.parent.values, value.StructValueField{
+ Name: s.name,
+ V: value.TzDateValueFromTime(v),
+ })
+
+ return s.parent
+}
diff --git a/internal/params/struct_test.go b/internal/params/struct_test.go
new file mode 100644
index 000000000..cad24fcaf
--- /dev/null
+++ b/internal/params/struct_test.go
@@ -0,0 +1,922 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestStruct(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ builder Builder
+ params map[string]*Ydb.TypedValue
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Uint64(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Int64(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Uint32(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Int32(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT32,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Uint16(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Int16(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Uint8(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT8,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Int8(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT8,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Bool(true).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Text("test").EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Bytes([]byte("test")).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_STRING,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Float(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_FLOAT,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Double(123).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DOUBLE,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: 123,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Interval(time.Second).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INTERVAL,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Datetime(time.Unix(123456789, 456)).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATETIME,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Date(time.Unix(123456789, 456)).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATE,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Timestamp(time.Unix(123456789, 456)).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TIMESTAMP,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").Decimal([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, 22, 9).EndStruct(), //nolint:lll
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").JSON(`{"a": 1,"b": "B"}`).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_JSON,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").JSONDocument(`{"a": 1,"b": "B"}`).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_JSON_DOCUMENT,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").YSON([]byte(`[ 1; 2; 3; 4; 5 ]`)).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_YSON,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`[ 1; 2; 3; 4; 5 ]`),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").
+ UUID([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UUID,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().
+ Field("col1").Text("text").
+ Field("col2").Uint32(123).
+ Field("col3").Int64(456).
+ EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "col2",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ },
+ {
+ Name: "col3",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "text",
+ },
+ },
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 456,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").TzDatetime(time.Unix(123456789, 456).UTC()).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TZ_DATETIME,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").TzDate(time.Unix(123456789, 456).UTC()).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TZ_DATE,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ builder: Builder{}.Param("$x").BeginStruct().Field("col1").TzTimestamp(time.Unix(123456789, 456).UTC()).EndStruct(),
+ params: map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_StructType{
+ StructType: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "col1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TZ_TIMESTAMP,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+ params := tt.builder.Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(tt.params), xtest.ToJSON(params))
+ })
+ }
+}
diff --git a/internal/params/tuple.go b/internal/params/tuple.go
new file mode 100644
index 000000000..7718a5026
--- /dev/null
+++ b/internal/params/tuple.go
@@ -0,0 +1,190 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ tuple struct {
+ parent Builder
+ name string
+ values []value.Value
+ }
+ tupleItem struct {
+ parent *tuple
+ }
+)
+
+func (t *tuple) Add() *tupleItem {
+ return &tupleItem{
+ parent: t,
+ }
+}
+
+func (t *tuple) AddItems(items ...value.Value) *tuple {
+ t.values = append(t.values, items...)
+
+ return t
+}
+
+func (t *tuple) EndTuple() Builder {
+ t.parent.params = append(t.parent.params, &Parameter{
+ parent: t.parent,
+ name: t.name,
+ value: value.TupleValue(t.values...),
+ })
+
+ return t.parent
+}
+
+func (t *tupleItem) Text(v string) *tuple {
+ t.parent.values = append(t.parent.values, value.TextValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Bytes(v []byte) *tuple {
+ t.parent.values = append(t.parent.values, value.BytesValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Bool(v bool) *tuple {
+ t.parent.values = append(t.parent.values, value.BoolValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Uint64(v uint64) *tuple {
+ t.parent.values = append(t.parent.values, value.Uint64Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Int64(v int64) *tuple {
+ t.parent.values = append(t.parent.values, value.Int64Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Uint32(v uint32) *tuple {
+ t.parent.values = append(t.parent.values, value.Uint32Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Int32(v int32) *tuple {
+ t.parent.values = append(t.parent.values, value.Int32Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Uint16(v uint16) *tuple {
+ t.parent.values = append(t.parent.values, value.Uint16Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Int16(v int16) *tuple {
+ t.parent.values = append(t.parent.values, value.Int16Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Uint8(v uint8) *tuple {
+ t.parent.values = append(t.parent.values, value.Uint8Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Int8(v int8) *tuple {
+ t.parent.values = append(t.parent.values, value.Int8Value(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Float(v float32) *tuple {
+ t.parent.values = append(t.parent.values, value.FloatValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Double(v float64) *tuple {
+ t.parent.values = append(t.parent.values, value.DoubleValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Decimal(v [16]byte, precision, scale uint32) *tuple {
+ t.parent.values = append(t.parent.values, value.DecimalValue(v, precision, scale))
+
+ return t.parent
+}
+
+func (t *tupleItem) Timestamp(v time.Time) *tuple {
+ t.parent.values = append(t.parent.values, value.TimestampValueFromTime(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Date(v time.Time) *tuple {
+ t.parent.values = append(t.parent.values, value.DateValueFromTime(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Datetime(v time.Time) *tuple {
+ t.parent.values = append(t.parent.values, value.DatetimeValueFromTime(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) Interval(v time.Duration) *tuple {
+ t.parent.values = append(t.parent.values, value.IntervalValueFromDuration(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) JSON(v string) *tuple {
+ t.parent.values = append(t.parent.values, value.JSONValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) JSONDocument(v string) *tuple {
+ t.parent.values = append(t.parent.values, value.JSONDocumentValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) YSON(v []byte) *tuple {
+ t.parent.values = append(t.parent.values, value.YSONValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) UUID(v [16]byte) *tuple {
+ t.parent.values = append(t.parent.values, value.UUIDValue(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) TzDate(v time.Time) *tuple {
+ t.parent.values = append(t.parent.values, value.TzDateValueFromTime(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) TzTimestamp(v time.Time) *tuple {
+ t.parent.values = append(t.parent.values, value.TzTimestampValueFromTime(v))
+
+ return t.parent
+}
+
+func (t *tupleItem) TzDatetime(v time.Time) *tuple {
+ t.parent.values = append(t.parent.values, value.TzDatetimeValueFromTime(v))
+
+ return t.parent
+}
diff --git a/internal/params/tuple_test.go b/internal/params/tuple_test.go
new file mode 100644
index 000000000..1c6510603
--- /dev/null
+++ b/internal/params/tuple_test.go
@@ -0,0 +1,488 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestTuple(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+ args []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ args: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int64",
+ args: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ args: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int32",
+ args: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ args: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int16",
+ args: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ args: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Int8",
+ args: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ },
+ {
+ method: "Bool",
+ args: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ {
+ method: "Text",
+ args: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ args: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ },
+ {
+ method: "Float",
+ args: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Double",
+ args: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ },
+ },
+ },
+ {
+ method: "Interval",
+ args: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ },
+ },
+ },
+ {
+ method: "Date",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ args: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ },
+ },
+ },
+ {
+ method: "JSON",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ args: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ },
+ },
+ },
+ {
+ method: "YSON",
+ args: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ },
+ },
+ },
+ {
+ method: "UUID",
+ args: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ args: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginTuple().Add()
+
+ result, ok := xtest.CallMethod(item, tc.method, tc.args...)[0].(*tuple)
+ require.True(t, ok)
+
+ params := result.EndTuple().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TupleType{
+ TupleType: &Ydb.TupleType{
+ Elements: []*Ydb.Type{
+ tc.expected.Type,
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ tc.expected.Value,
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
+
+func TestTuple_AddItems(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+ params := Builder{}.Param("$x").BeginTuple().
+ AddItems(value.Uint64Value(123), value.Uint64Value(321)).
+ EndTuple().Build().ToYDB(a)
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TupleType{
+ TupleType: &Ydb.TupleType{
+ Elements: []*Ydb.Type{
+ {
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ {
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Items: []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 321,
+ },
+ },
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+}
diff --git a/internal/params/variant.go b/internal/params/variant.go
new file mode 100644
index 000000000..16815e38e
--- /dev/null
+++ b/internal/params/variant.go
@@ -0,0 +1,37 @@
+package params
+
+import "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+
+type (
+ variant struct {
+ parent Builder
+ name string
+ value value.Value
+ }
+
+ variantBuilder struct {
+ variant *variant
+ }
+)
+
+func (vb *variantBuilder) EndVariant() Builder {
+ vb.variant.parent.params = append(vb.variant.parent.params, &Parameter{
+ parent: vb.variant.parent,
+ name: vb.variant.name,
+ value: vb.variant.value,
+ })
+
+ return vb.variant.parent
+}
+
+func (v *variant) BeginTuple() *variantTuple {
+ return &variantTuple{
+ parent: v,
+ }
+}
+
+func (v *variant) BeginStruct() *variantStruct {
+ return &variantStruct{
+ parent: v,
+ }
+}
diff --git a/internal/params/variant_struct.go b/internal/params/variant_struct.go
new file mode 100644
index 000000000..190e7d85f
--- /dev/null
+++ b/internal/params/variant_struct.go
@@ -0,0 +1,489 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ variantStruct struct {
+ parent *variant
+
+ fields []types.StructField
+ name string
+ value value.Value
+ }
+
+ variantStructField struct {
+ name string
+ parent *variantStruct
+ }
+
+ variantStructItem struct {
+ parent *variantStruct
+ }
+
+ variantStructBuilder struct {
+ parent *variantStruct
+ }
+)
+
+func (vs *variantStruct) Field(name string) *variantStructField {
+ return &variantStructField{
+ name: name,
+ parent: vs,
+ }
+}
+
+func (vs *variantStruct) AddFields(args ...types.StructField) *variantStruct {
+ vs.fields = append(vs.fields, args...)
+
+ return vs
+}
+
+func (vsf *variantStructField) Text() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Text,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Bytes() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Bytes,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Bool() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Bool,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Uint64() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Uint64,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Int64() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Int64,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Uint32() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Uint32,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Int32() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Int32,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Uint16() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Uint16,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Int16() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Int16,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Uint8() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Uint8,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Int8() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Int8,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Float() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Float,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Double() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Double,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Decimal(precision, scale uint32) *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.NewDecimal(precision, scale),
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Timestamp() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Timestamp,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Date() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Date,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Datetime() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Datetime,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) Interval() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.Interval,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) JSON() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.JSON,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) JSONDocument() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.JSONDocument,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) YSON() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.YSON,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) UUID() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.UUID,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) TzDate() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.TzDate,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) TzTimestamp() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.TzTimestamp,
+ })
+
+ return vsf.parent
+}
+
+func (vsf *variantStructField) TzDatetime() *variantStruct {
+ vsf.parent.fields = append(vsf.parent.fields, types.StructField{
+ Name: vsf.name,
+ T: types.TzDatetime,
+ })
+
+ return vsf.parent
+}
+
+func (vs *variantStruct) Name(name string) *variantStructItem {
+ vs.name = name
+
+ return &variantStructItem{
+ parent: vs,
+ }
+}
+
+func (vsi *variantStructItem) Text(v string) *variantStructBuilder {
+ vsi.parent.value = value.TextValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Bytes(v []byte) *variantStructBuilder {
+ vsi.parent.value = value.BytesValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Bool(v bool) *variantStructBuilder {
+ vsi.parent.value = value.BoolValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Uint64(v uint64) *variantStructBuilder {
+ vsi.parent.value = value.Uint64Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Int64(v int64) *variantStructBuilder {
+ vsi.parent.value = value.Int64Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Uint32(v uint32) *variantStructBuilder {
+ vsi.parent.value = value.Uint32Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Int32(v int32) *variantStructBuilder {
+ vsi.parent.value = value.Int32Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Uint16(v uint16) *variantStructBuilder {
+ vsi.parent.value = value.Uint16Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Int16(v int16) *variantStructBuilder {
+ vsi.parent.value = value.Int16Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Uint8(v uint8) *variantStructBuilder {
+ vsi.parent.value = value.Uint8Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Int8(v int8) *variantStructBuilder {
+ vsi.parent.value = value.Int8Value(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Float(v float32) *variantStructBuilder {
+ vsi.parent.value = value.FloatValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Double(v float64) *variantStructBuilder {
+ vsi.parent.value = value.DoubleValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Decimal(v [16]byte, precision, scale uint32) *variantStructBuilder {
+ vsi.parent.value = value.DecimalValue(v, precision, scale)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Timestamp(v time.Time) *variantStructBuilder {
+ vsi.parent.value = value.TimestampValueFromTime(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Date(v time.Time) *variantStructBuilder {
+ vsi.parent.value = value.DateValueFromTime(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Datetime(v time.Time) *variantStructBuilder {
+ vsi.parent.value = value.DatetimeValueFromTime(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) Interval(v time.Duration) *variantStructBuilder {
+ vsi.parent.value = value.IntervalValueFromDuration(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) JSON(v string) *variantStructBuilder {
+ vsi.parent.value = value.JSONValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) JSONDocument(v string) *variantStructBuilder {
+ vsi.parent.value = value.JSONDocumentValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) YSON(v []byte) *variantStructBuilder {
+ vsi.parent.value = value.YSONValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) UUID(v [16]byte) *variantStructBuilder {
+ vsi.parent.value = value.UUIDValue(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) TzDate(v time.Time) *variantStructBuilder {
+ vsi.parent.value = value.TzDateValueFromTime(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) TzTimestamp(v time.Time) *variantStructBuilder {
+ vsi.parent.value = value.TzTimestampValueFromTime(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsi *variantStructItem) TzDatetime(v time.Time) *variantStructBuilder {
+ vsi.parent.value = value.TzDatetimeValueFromTime(v)
+
+ return &variantStructBuilder{
+ parent: vsi.parent,
+ }
+}
+
+func (vsb *variantStructBuilder) EndStruct() *variantBuilder {
+ vsb.parent.parent.value = value.VariantValueStruct(
+ vsb.parent.value,
+ vsb.parent.name,
+ types.NewVariantStruct(vsb.parent.fields...),
+ )
+
+ return &variantBuilder{
+ variant: vsb.parent.parent,
+ }
+}
diff --git a/internal/params/variant_struct_test.go b/internal/params/variant_struct_test.go
new file mode 100644
index 000000000..cf8330da0
--- /dev/null
+++ b/internal/params/variant_struct_test.go
@@ -0,0 +1,557 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestVariantStruct(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+
+ typeArgs []any
+ itemArgs []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ itemArgs: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int64",
+ itemArgs: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ itemArgs: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int32",
+ itemArgs: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ itemArgs: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int16",
+ itemArgs: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ itemArgs: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int8",
+ itemArgs: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Bool",
+ itemArgs: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Text",
+ itemArgs: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ itemArgs: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Float",
+ itemArgs: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Double",
+ itemArgs: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Interval",
+ itemArgs: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ itemArgs: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Date",
+ itemArgs: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ itemArgs: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ typeArgs: []any{uint32(22), uint32(9)},
+ itemArgs: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "JSON",
+ itemArgs: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ itemArgs: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "YSON",
+ itemArgs: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "UUID",
+ itemArgs: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ itemArgs: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ itemArgs: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ itemArgs: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginVariant().BeginStruct().Field("key")
+
+ vs, ok := xtest.CallMethod(item, tc.method, tc.typeArgs...)[0].(*variantStruct)
+ require.True(t, ok)
+
+ builder, ok := xtest.CallMethod(vs.Name("key"), tc.method, tc.itemArgs...)[0].(*variantStructBuilder)
+ require.True(t, ok)
+
+ params := builder.EndStruct().EndVariant().Build().ToYDB(a)
+
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_VariantType{
+ VariantType: &Ydb.VariantType{
+ Type: &Ydb.VariantType_StructItems{
+ StructItems: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "key",
+ Type: tc.expected.Type,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_NestedValue{
+ NestedValue: tc.expected.Value,
+ },
+ VariantIndex: 0,
+ },
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
+
+func TestVariantStruct_AddFields(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ params := Builder{}.Param("$x").BeginVariant().BeginStruct().
+ AddFields([]types.StructField{
+ {
+ Name: "key1",
+ T: types.Bool,
+ },
+ {
+ Name: "key2",
+ T: types.Uint64,
+ },
+ {
+ Name: "key3",
+ T: types.Text,
+ },
+ }...).Name("key3").Text("Hello, World!").EndStruct().
+ EndVariant().Build().ToYDB(a)
+
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_VariantType{
+ VariantType: &Ydb.VariantType{
+ Type: &Ydb.VariantType_StructItems{
+ StructItems: &Ydb.StructType{
+ Members: []*Ydb.StructMember{
+ {
+ Name: "key1",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ {
+ Name: "key2",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "key3",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_NestedValue{
+ NestedValue: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "Hello, World!",
+ },
+ },
+ },
+ VariantIndex: 2,
+ },
+ },
+ }), xtest.ToJSON(params))
+}
diff --git a/internal/params/variant_tuple.go b/internal/params/variant_tuple.go
new file mode 100644
index 000000000..9fea4fdaa
--- /dev/null
+++ b/internal/params/variant_tuple.go
@@ -0,0 +1,412 @@
+package params
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+type (
+ variantTuple struct {
+ parent *variant
+
+ types []types.Type
+ index uint32
+ value value.Value
+ }
+
+ variantTupleTypes struct {
+ tuple *variantTuple
+ }
+
+ variantTupleItem struct {
+ tuple *variantTuple
+ }
+
+ variantTupleBuilder struct {
+ tuple *variantTuple
+ }
+)
+
+func (vt *variantTuple) Types() *variantTupleTypes {
+ return &variantTupleTypes{
+ tuple: vt,
+ }
+}
+
+func (vtt *variantTupleTypes) AddTypes(args ...types.Type) *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, args...)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Text() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Text)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Bytes() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Bytes)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Bool() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Bool)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Uint64() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Uint64)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Int64() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Int64)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Uint32() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Uint32)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Int32() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Int32)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Uint16() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Uint16)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Int16() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Int16)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Uint8() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Uint8)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Int8() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Int8)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Float() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Float)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Double() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Double)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Decimal(precision, scale uint32) *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.NewDecimal(precision, scale))
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Timestamp() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Timestamp)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Date() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Date)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Datetime() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Datetime)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Interval() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.Interval)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) JSON() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.JSON)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) JSONDocument() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.JSONDocument)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) YSON() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.YSON)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) UUID() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.UUID)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) TzDate() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.TzDate)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) TzTimestamp() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.TzTimestamp)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) TzDatetime() *variantTupleTypes {
+ vtt.tuple.types = append(vtt.tuple.types, types.TzDatetime)
+
+ return vtt
+}
+
+func (vtt *variantTupleTypes) Index(i uint32) *variantTupleItem {
+ vtt.tuple.index = i
+
+ return &variantTupleItem{
+ tuple: vtt.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Text(v string) *variantTupleBuilder {
+ vti.tuple.value = value.TextValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Bytes(v []byte) *variantTupleBuilder {
+ vti.tuple.value = value.BytesValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Bool(v bool) *variantTupleBuilder {
+ vti.tuple.value = value.BoolValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Uint64(v uint64) *variantTupleBuilder {
+ vti.tuple.value = value.Uint64Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Int64(v int64) *variantTupleBuilder {
+ vti.tuple.value = value.Int64Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Uint32(v uint32) *variantTupleBuilder {
+ vti.tuple.value = value.Uint32Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Int32(v int32) *variantTupleBuilder {
+ vti.tuple.value = value.Int32Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Uint16(v uint16) *variantTupleBuilder {
+ vti.tuple.value = value.Uint16Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Int16(v int16) *variantTupleBuilder {
+ vti.tuple.value = value.Int16Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Uint8(v uint8) *variantTupleBuilder {
+ vti.tuple.value = value.Uint8Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Int8(v int8) *variantTupleBuilder {
+ vti.tuple.value = value.Int8Value(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Float(v float32) *variantTupleBuilder {
+ vti.tuple.value = value.FloatValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Double(v float64) *variantTupleBuilder {
+ vti.tuple.value = value.DoubleValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Decimal(v [16]byte, precision, scale uint32) *variantTupleBuilder {
+ vti.tuple.value = value.DecimalValue(v, precision, scale)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Timestamp(v time.Time) *variantTupleBuilder {
+ vti.tuple.value = value.TimestampValueFromTime(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Date(v time.Time) *variantTupleBuilder {
+ vti.tuple.value = value.DateValueFromTime(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Datetime(v time.Time) *variantTupleBuilder {
+ vti.tuple.value = value.DatetimeValueFromTime(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) Interval(v time.Duration) *variantTupleBuilder {
+ vti.tuple.value = value.IntervalValueFromDuration(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) JSON(v string) *variantTupleBuilder {
+ vti.tuple.value = value.JSONValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) JSONDocument(v string) *variantTupleBuilder {
+ vti.tuple.value = value.JSONDocumentValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) YSON(v []byte) *variantTupleBuilder {
+ vti.tuple.value = value.YSONValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) UUID(v [16]byte) *variantTupleBuilder {
+ vti.tuple.value = value.UUIDValue(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) TzDate(v time.Time) *variantTupleBuilder {
+ vti.tuple.value = value.TzDateValueFromTime(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) TzTimestamp(v time.Time) *variantTupleBuilder {
+ vti.tuple.value = value.TzTimestampValueFromTime(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vti *variantTupleItem) TzDatetime(v time.Time) *variantTupleBuilder {
+ vti.tuple.value = value.TzDatetimeValueFromTime(v)
+
+ return &variantTupleBuilder{
+ tuple: vti.tuple,
+ }
+}
+
+func (vtb *variantTupleBuilder) EndTuple() *variantBuilder {
+ vtb.tuple.parent.value = value.VariantValueTuple(
+ vtb.tuple.value,
+ vtb.tuple.index,
+ types.NewVariantTuple(vtb.tuple.types...),
+ )
+
+ return &variantBuilder{
+ variant: vtb.tuple.parent,
+ }
+}
diff --git a/internal/params/variant_tuple_test.go b/internal/params/variant_tuple_test.go
new file mode 100644
index 000000000..b24480d38
--- /dev/null
+++ b/internal/params/variant_tuple_test.go
@@ -0,0 +1,528 @@
+package params
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestVariantTuple(t *testing.T) {
+ type expected struct {
+ Type *Ydb.Type
+ Value *Ydb.Value
+ }
+
+ tests := []struct {
+ method string
+
+ typeArgs []any
+ itemArgs []any
+
+ expected expected
+ }{
+ {
+ method: "Uint64",
+ itemArgs: []any{uint64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int64",
+ itemArgs: []any{int64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Uint32",
+ itemArgs: []any{uint32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int32",
+ itemArgs: []any{int32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Uint16",
+ itemArgs: []any{uint16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int16",
+ itemArgs: []any{int16(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Uint8",
+ itemArgs: []any{uint8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Int8",
+ itemArgs: []any{int8(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Bool",
+ itemArgs: []any{true},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Text",
+ itemArgs: []any{"test"},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Bytes",
+ itemArgs: []any{[]byte("test")},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Float",
+ itemArgs: []any{float32(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_FloatValue{
+ FloatValue: float32(123),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Double",
+ itemArgs: []any{float64(123)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_DoubleValue{
+ DoubleValue: float64(123),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Interval",
+ itemArgs: []any{time.Second},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 1000000,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Datetime",
+ itemArgs: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123456789,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Date",
+ itemArgs: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 1428,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Timestamp",
+ itemArgs: []any{time.Unix(123456789, 456)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123456789000000,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "Decimal",
+ typeArgs: []any{uint32(22), uint32(9)},
+ itemArgs: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, uint32(22), uint32(9)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_DecimalType{
+ DecimalType: &Ydb.DecimalType{
+ Precision: 22,
+ Scale: 9,
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ High_128: 72623859790382856,
+ Value: &Ydb.Value_Low_128{
+ Low_128: 648519454493508870,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "JSON",
+ itemArgs: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "JSONDocument",
+ itemArgs: []any{`{"a": 1,"b": "B"}`},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: `{"a": 1,"b": "B"}`,
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "YSON",
+ itemArgs: []any{[]byte(`{"a": 1,"b": "B"}`)},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte(`{"a": 1,"b": "B"}`),
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "UUID",
+ itemArgs: []any{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_Low_128{
+ Low_128: 651345242494996240,
+ },
+ High_128: 72623859790382856,
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "TzDatetime",
+ itemArgs: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09Z",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "TzDate",
+ itemArgs: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ {
+ method: "TzTimestamp",
+ itemArgs: []any{time.Unix(123456789, 456).UTC()},
+
+ expected: expected{
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP},
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1973-11-29T21:33:09.000000Z",
+ },
+ VariantIndex: 0,
+ },
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.method, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ item := Builder{}.Param("$x").BeginVariant().BeginTuple().Types()
+
+ types, ok := xtest.CallMethod(item, tc.method, tc.typeArgs...)[0].(*variantTupleTypes)
+ require.True(t, ok)
+
+ builder, ok := xtest.CallMethod(types.Index(0), tc.method, tc.itemArgs...)[0].(*variantTupleBuilder)
+ require.True(t, ok)
+
+ params := builder.EndTuple().EndVariant().Build().ToYDB(a)
+
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_VariantType{
+ VariantType: &Ydb.VariantType{
+ Type: &Ydb.VariantType_TupleItems{
+ TupleItems: &Ydb.TupleType{
+ Elements: []*Ydb.Type{
+ tc.expected.Type,
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_NestedValue{
+ NestedValue: tc.expected.Value,
+ },
+ },
+ },
+ }), xtest.ToJSON(params))
+ })
+ }
+}
+
+func TestVariantTuple_AddTypes(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+
+ params := Builder{}.Param("$x").BeginVariant().BeginTuple().
+ Types().AddTypes(types.Int64, types.Bool).
+ Index(1).
+ Bool(true).
+ EndTuple().EndVariant().Build().ToYDB(a)
+
+ require.Equal(t, xtest.ToJSON(
+ map[string]*Ydb.TypedValue{
+ "$x": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_VariantType{
+ VariantType: &Ydb.VariantType{
+ Type: &Ydb.VariantType_TupleItems{
+ TupleItems: &Ydb.TupleType{
+ Elements: []*Ydb.Type{
+ {
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ {
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_NestedValue{
+ NestedValue: &Ydb.Value{
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ VariantIndex: 1,
+ },
+ },
+ }), xtest.ToJSON(params))
+}
diff --git a/internal/pg/pgconst.go b/internal/pg/pgconst.go
new file mode 100644
index 000000000..f6a8273c8
--- /dev/null
+++ b/internal/pg/pgconst.go
@@ -0,0 +1,9 @@
+package pg
+
+const (
+ // https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_type.dat
+
+ OIDInt4 = 23
+ OIDInt8 = 20
+ OIDUnknown = 705
+)
diff --git a/internal/pool/defaults.go b/internal/pool/defaults.go
new file mode 100644
index 000000000..2591e8438
--- /dev/null
+++ b/internal/pool/defaults.go
@@ -0,0 +1,31 @@
+package pool
+
+const DefaultLimit = 50
+
+var defaultTrace = &Trace{
+ OnNew: func(info *NewStartInfo) func(info *NewDoneInfo) {
+ return func(info *NewDoneInfo) {
+ }
+ },
+ OnClose: func(info *CloseStartInfo) func(info *CloseDoneInfo) {
+ return func(info *CloseDoneInfo) {
+ }
+ },
+ OnTry: func(info *TryStartInfo) func(info *TryDoneInfo) {
+ return func(info *TryDoneInfo) {
+ }
+ },
+ OnWith: func(info *WithStartInfo) func(info *WithDoneInfo) {
+ return func(info *WithDoneInfo) {
+ }
+ },
+ OnPut: func(info *PutStartInfo) func(info *PutDoneInfo) {
+ return func(info *PutDoneInfo) {
+ }
+ },
+ OnGet: func(info *GetStartInfo) func(info *GetDoneInfo) {
+ return func(info *GetDoneInfo) {
+ }
+ },
+ OnChange: func(info ChangeInfo) {},
+}
diff --git a/internal/pool/errors.go b/internal/pool/errors.go
new file mode 100644
index 000000000..36b6526c7
--- /dev/null
+++ b/internal/pool/errors.go
@@ -0,0 +1,10 @@
+package pool
+
+import (
+ "errors"
+)
+
+var (
+ errClosedPool = errors.New("closed pool")
+ errItemIsNotAlive = errors.New("item is not alive")
+)
diff --git a/internal/pool/pool.go b/internal/pool/pool.go
new file mode 100644
index 000000000..b7a5e8a3f
--- /dev/null
+++ b/internal/pool/pool.go
@@ -0,0 +1,484 @@
+package pool
+
+import (
+ "context"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool/stats"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
+ "github.com/ydb-platform/ydb-go-sdk/v3/retry"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+type (
+ Item[T any] interface {
+ *T
+ IsAlive() bool
+ Close(ctx context.Context) error
+ }
+ safeStats struct {
+ mu xsync.RWMutex
+ v stats.Stats
+ onChange func(stats.Stats)
+ }
+ statsItemAddr struct {
+ v *int
+ onChange func(func())
+ }
+ Pool[PT Item[T], T any] struct {
+ trace *Trace
+ limit int
+
+ createItem func(ctx context.Context) (PT, error)
+ createTimeout time.Duration
+ closeTimeout time.Duration
+
+ mu xsync.Mutex
+ idle []PT
+ index map[PT]struct{}
+ done chan struct{}
+
+ stats *safeStats
+ }
+ option[PT Item[T], T any] func(p *Pool[PT, T])
+)
+
+func (field statsItemAddr) Inc() {
+ field.onChange(func() {
+ *field.v++
+ })
+}
+
+func (field statsItemAddr) Dec() {
+ field.onChange(func() {
+ *field.v--
+ })
+}
+
+func (s *safeStats) Get() stats.Stats {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return s.v
+}
+
+func (s *safeStats) Index() statsItemAddr {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return statsItemAddr{
+ v: &s.v.Index,
+ onChange: func(f func()) {
+ s.mu.WithLock(f)
+ if s.onChange != nil {
+ s.onChange(s.Get())
+ }
+ },
+ }
+}
+
+func (s *safeStats) Idle() statsItemAddr {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return statsItemAddr{
+ v: &s.v.Idle,
+ onChange: func(f func()) {
+ s.mu.WithLock(f)
+ if s.onChange != nil {
+ s.onChange(s.Get())
+ }
+ },
+ }
+}
+
+func (s *safeStats) InUse() statsItemAddr {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return statsItemAddr{
+ v: &s.v.InUse,
+ onChange: func(f func()) {
+ s.mu.WithLock(f)
+ if s.onChange != nil {
+ s.onChange(s.Get())
+ }
+ },
+ }
+}
+
+func WithCreateFunc[PT Item[T], T any](f func(ctx context.Context) (PT, error)) option[PT, T] {
+ return func(p *Pool[PT, T]) {
+ p.createItem = f
+ }
+}
+
+func WithCreateItemTimeout[PT Item[T], T any](t time.Duration) option[PT, T] {
+ return func(p *Pool[PT, T]) {
+ p.createTimeout = t
+ }
+}
+
+func WithCloseItemTimeout[PT Item[T], T any](t time.Duration) option[PT, T] {
+ return func(p *Pool[PT, T]) {
+ p.closeTimeout = t
+ }
+}
+
+func WithLimit[PT Item[T], T any](size int) option[PT, T] {
+ return func(p *Pool[PT, T]) {
+ p.limit = size
+ }
+}
+
+func WithTrace[PT Item[T], T any](t *Trace) option[PT, T] {
+ return func(p *Pool[PT, T]) {
+ p.trace = t
+ }
+}
+
+func New[PT Item[T], T any](
+ ctx context.Context,
+ opts ...option[PT, T],
+) *Pool[PT, T] {
+ p := &Pool[PT, T]{
+ trace: defaultTrace,
+ limit: DefaultLimit,
+ createItem: func(ctx context.Context) (PT, error) {
+ var item T
+
+ return &item, nil
+ },
+ done: make(chan struct{}),
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(p)
+ }
+ }
+
+ onDone := p.trace.OnNew(&NewStartInfo{
+ Context: &ctx,
+ Call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/pool.New"),
+ })
+
+ defer func() {
+ onDone(&NewDoneInfo{
+ Limit: p.limit,
+ })
+ }()
+
+ createItem := p.createItem
+
+ p.createItem = func(ctx context.Context) (PT, error) {
+ var (
+ ch = make(chan PT)
+ createErr error
+ )
+ go func() {
+ defer close(ch)
+ createErr = func() error {
+ var (
+ createCtx = xcontext.ValueOnly(ctx)
+ cancelCreate context.CancelFunc
+ )
+ if d := p.createTimeout; d > 0 {
+ createCtx, cancelCreate = xcontext.WithTimeout(createCtx, d)
+ } else {
+ createCtx, cancelCreate = xcontext.WithCancel(createCtx)
+ }
+ defer cancelCreate()
+
+ newItem, err := createItem(createCtx)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ needCloseItem := true
+ defer func() {
+ if needCloseItem {
+ _ = p.closeItem(ctx, newItem)
+ }
+ }()
+
+ select {
+ case <-p.done:
+ return xerrors.WithStackTrace(errClosedPool)
+
+ case <-ctx.Done():
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ if len(p.index) < p.limit {
+ p.idle = append(p.idle, newItem)
+ p.index[newItem] = struct{}{}
+ p.stats.Index().Inc()
+ needCloseItem = false
+ }
+
+ return xerrors.WithStackTrace(ctx.Err())
+
+ case ch <- newItem:
+ needCloseItem = false
+
+ return nil
+ }
+ }()
+ }()
+
+ select {
+ case <-p.done:
+ return nil, xerrors.WithStackTrace(errClosedPool)
+ case <-ctx.Done():
+ return nil, xerrors.WithStackTrace(ctx.Err())
+ case item, has := <-ch:
+ if !has {
+ if ctxErr := ctx.Err(); ctxErr == nil && xerrors.IsContextError(createErr) {
+ return nil, xerrors.WithStackTrace(xerrors.Retryable(createErr))
+ }
+
+ return nil, xerrors.WithStackTrace(createErr)
+ }
+
+ return item, nil
+ }
+ }
+ p.idle = make([]PT, 0, p.limit)
+ p.index = make(map[PT]struct{}, p.limit)
+ p.stats = &safeStats{
+ v: stats.Stats{Limit: p.limit},
+ onChange: p.trace.OnChange,
+ }
+
+ return p
+}
+
+func (p *Pool[PT, T]) Stats() stats.Stats {
+ return p.stats.Get()
+}
+
+func (p *Pool[PT, T]) getItem(ctx context.Context) (_ PT, finalErr error) {
+ onDone := p.trace.OnGet(&GetStartInfo{
+ Context: &ctx,
+ Call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/pool.(*Pool).getItem"),
+ })
+ defer func() {
+ onDone(&GetDoneInfo{
+ Error: finalErr,
+ })
+ }()
+
+ if err := ctx.Err(); err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ select {
+ case <-p.done:
+ return nil, xerrors.WithStackTrace(errClosedPool)
+ case <-ctx.Done():
+ return nil, xerrors.WithStackTrace(ctx.Err())
+ default:
+ var item PT
+ p.mu.WithLock(func() {
+ if len(p.idle) > 0 {
+ item, p.idle = p.idle[0], p.idle[1:]
+ p.stats.Idle().Dec()
+ }
+ })
+
+ if item != nil {
+ if item.IsAlive() {
+ return item, nil
+ }
+ _ = p.closeItem(ctx, item)
+ p.mu.WithLock(func() {
+ delete(p.index, item)
+ })
+ p.stats.Index().Dec()
+ }
+
+ item, err := p.createItem(ctx)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ addedToIndex := false
+ p.mu.WithLock(func() {
+ if len(p.index) < p.limit {
+ p.index[item] = struct{}{}
+ addedToIndex = true
+ }
+ })
+ if addedToIndex {
+ p.stats.Index().Inc()
+ }
+
+ return item, nil
+ }
+}
+
+func (p *Pool[PT, T]) putItem(ctx context.Context, item PT) (finalErr error) {
+ onDone := p.trace.OnPut(&PutStartInfo{
+ Context: &ctx,
+ Call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/pool.(*Pool).putItem"),
+ })
+ defer func() {
+ onDone(&PutDoneInfo{
+ Error: finalErr,
+ })
+ }()
+
+ if err := ctx.Err(); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ select {
+ case <-p.done:
+ return xerrors.WithStackTrace(errClosedPool)
+ default:
+ if !item.IsAlive() {
+ _ = p.closeItem(ctx, item)
+
+ p.mu.WithLock(func() {
+ delete(p.index, item)
+ })
+ p.stats.Index().Dec()
+
+ return xerrors.WithStackTrace(errItemIsNotAlive)
+ }
+
+ p.mu.WithLock(func() {
+ p.idle = append(p.idle, item)
+ })
+ p.stats.Idle().Inc()
+
+ return nil
+ }
+}
+
+func (p *Pool[PT, T]) closeItem(ctx context.Context, item PT) error {
+ ctx = xcontext.ValueOnly(ctx)
+
+ var cancel context.CancelFunc
+ if d := p.closeTimeout; d > 0 {
+ ctx, cancel = xcontext.WithTimeout(ctx, d)
+ } else {
+ ctx, cancel = xcontext.WithCancel(ctx)
+ }
+ defer cancel()
+
+ return item.Close(ctx)
+}
+
+func (p *Pool[PT, T]) try(ctx context.Context, f func(ctx context.Context, item PT) error) (finalErr error) {
+ onDone := p.trace.OnTry(&TryStartInfo{
+ Context: &ctx,
+ Call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/pool.(*Pool).try"),
+ })
+ defer func() {
+ onDone(&TryDoneInfo{
+ Error: finalErr,
+ })
+ }()
+
+ item, err := p.getItem(ctx)
+ if err != nil {
+ if xerrors.IsYdb(err) {
+ return xerrors.WithStackTrace(xerrors.Retryable(err))
+ }
+
+ return xerrors.WithStackTrace(err)
+ }
+
+ defer func() {
+ _ = p.putItem(ctx, item)
+ }()
+
+ p.stats.InUse().Inc()
+ defer p.stats.InUse().Dec()
+
+ err = f(ctx, item)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+}
+
+func (p *Pool[PT, T]) With(
+ ctx context.Context,
+ f func(ctx context.Context, item PT) error,
+ opts ...retry.Option,
+) (finalErr error) {
+ var (
+ onDone = p.trace.OnWith(&WithStartInfo{
+ Context: &ctx,
+ Call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/pool.(*Pool).With"),
+ })
+ attempts int
+ )
+ defer func() {
+ onDone(&WithDoneInfo{
+ Error: finalErr,
+ Attempts: attempts,
+ })
+ }()
+
+ err := retry.Retry(ctx, func(ctx context.Context) error {
+ err := p.try(ctx, f)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+ }, append(opts, retry.WithTrace(&trace.Retry{
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
+ }
+ },
+ }))...)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+}
+
+func (p *Pool[PT, T]) Close(ctx context.Context) (finalErr error) {
+ onDone := p.trace.OnClose(&CloseStartInfo{
+ Context: &ctx,
+ Call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/pool.(*Pool).Close"),
+ })
+ defer func() {
+ onDone(&CloseDoneInfo{
+ Error: finalErr,
+ })
+ }()
+
+ close(p.done)
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ errs := make([]error, 0, len(p.index))
+
+ for item := range p.index {
+ if err := item.Close(ctx); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ switch len(errs) {
+ case 0:
+ return nil
+ case 1:
+ return errs[0]
+ default:
+ return xerrors.Join(errs...)
+ }
+}
diff --git a/internal/pool/pool_test.go b/internal/pool/pool_test.go
new file mode 100644
index 000000000..63f8a1c11
--- /dev/null
+++ b/internal/pool/pool_test.go
@@ -0,0 +1,320 @@
+package pool
+
+import (
+ "context"
+ "errors"
+ "math/rand"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ grpcCodes "google.golang.org/grpc/codes"
+ grpcStatus "google.golang.org/grpc/status"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+type testItem struct {
+ v uint32
+
+ onClose func() error
+ onIsAlive func() bool
+}
+
+func (t testItem) IsAlive() bool {
+ if t.onIsAlive != nil {
+ return t.onIsAlive()
+ }
+
+ return true
+}
+
+func (t testItem) ID() string {
+ return ""
+}
+
+func (t testItem) Close(context.Context) error {
+ if t.onClose != nil {
+ return t.onClose()
+ }
+
+ return nil
+}
+
+func TestPool(t *testing.T) {
+ rootCtx := xtest.Context(t)
+ t.Run("New", func(t *testing.T) {
+ t.Run("Default", func(t *testing.T) {
+ p := New[*testItem, testItem](rootCtx)
+ err := p.With(rootCtx, func(ctx context.Context, testItem *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ })
+ t.Run("WithLimit", func(t *testing.T) {
+ p := New[*testItem, testItem](rootCtx, WithLimit[*testItem, testItem](1))
+ require.EqualValues(t, 1, p.limit)
+ })
+ t.Run("WithCreateFunc", func(t *testing.T) {
+ var newCounter int64
+ p := New(rootCtx,
+ WithLimit[*testItem, testItem](1),
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&newCounter, 1)
+ var v testItem
+
+ return &v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, item *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ require.EqualValues(t, p.limit, atomic.LoadInt64(&newCounter))
+ })
+ })
+ t.Run("Retry", func(t *testing.T) {
+ t.Run("CreateItem", func(t *testing.T) {
+ t.Run("context", func(t *testing.T) {
+ t.Run("Cancelled", func(t *testing.T) {
+ var counter int64
+ p := New(rootCtx,
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&counter, 1)
+
+ if atomic.LoadInt64(&counter) < 10 {
+ return nil, context.Canceled
+ }
+
+ var v testItem
+
+ return &v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, item *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, atomic.LoadInt64(&counter), int64(10))
+ })
+ t.Run("DeadlineExceeded", func(t *testing.T) {
+ var counter int64
+ p := New(rootCtx,
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&counter, 1)
+
+ if atomic.LoadInt64(&counter) < 10 {
+ return nil, context.DeadlineExceeded
+ }
+
+ var v testItem
+
+ return &v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, item *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, atomic.LoadInt64(&counter), int64(10))
+ })
+ })
+ t.Run("OnTransportError", func(t *testing.T) {
+ var counter int64
+ p := New(rootCtx,
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&counter, 1)
+
+ if atomic.LoadInt64(&counter) < 10 {
+ return nil, xerrors.Transport(grpcStatus.Error(grpcCodes.Unavailable, ""))
+ }
+
+ var v testItem
+
+ return &v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, item *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, atomic.LoadInt64(&counter), int64(10))
+ })
+ t.Run("OnOperationError", func(t *testing.T) {
+ var counter int64
+ p := New(rootCtx,
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&counter, 1)
+
+ if atomic.LoadInt64(&counter) < 10 {
+ return nil, xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE))
+ }
+
+ var v testItem
+
+ return &v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, item *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, atomic.LoadInt64(&counter), int64(10))
+ })
+ })
+ })
+ t.Run("On", func(t *testing.T) {
+ t.Run("Context", func(t *testing.T) {
+ t.Run("Canceled", func(t *testing.T) {
+ ctx, cancel := context.WithCancel(rootCtx)
+ cancel()
+ p := New[*testItem, testItem](ctx, WithLimit[*testItem, testItem](1))
+ err := p.With(ctx, func(ctx context.Context, testItem *testItem) error {
+ return nil
+ })
+ require.ErrorIs(t, err, context.Canceled)
+ })
+ t.Run("DeadlineExceeded", func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(rootCtx, 0)
+ cancel()
+ p := New[*testItem, testItem](ctx, WithLimit[*testItem, testItem](1))
+ err := p.With(ctx, func(ctx context.Context, testItem *testItem) error {
+ return nil
+ })
+ require.ErrorIs(t, err, context.DeadlineExceeded)
+ })
+ })
+ })
+ t.Run("Item", func(t *testing.T) {
+ t.Run("Close", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ var (
+ createCounter int64
+ closeCounter int64
+ )
+ p := New(rootCtx,
+ WithLimit[*testItem, testItem](1),
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&createCounter, 1)
+
+ v := &testItem{
+ onClose: func() error {
+ atomic.AddInt64(&closeCounter, 1)
+
+ return nil
+ },
+ }
+
+ return v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, testItem *testItem) error {
+ return nil
+ })
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, atomic.LoadInt64(&createCounter), atomic.LoadInt64(&closeCounter))
+ err = p.Close(rootCtx)
+ require.NoError(t, err)
+ require.EqualValues(t, atomic.LoadInt64(&createCounter), atomic.LoadInt64(&closeCounter))
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("IsAlive", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ var (
+ newItems int64
+ deleteItems int64
+ expErr = xerrors.Retryable(errors.New("expected error"), xerrors.InvalidObject())
+ )
+ p := New(rootCtx,
+ WithLimit[*testItem, testItem](1),
+ WithCreateFunc(func(context.Context) (*testItem, error) {
+ atomic.AddInt64(&newItems, 1)
+
+ v := &testItem{
+ onClose: func() error {
+ atomic.AddInt64(&deleteItems, 1)
+
+ return nil
+ },
+ onIsAlive: func() bool {
+ return atomic.LoadInt64(&newItems) >= 10
+ },
+ }
+
+ return v, nil
+ }),
+ )
+ err := p.With(rootCtx, func(ctx context.Context, testItem *testItem) error {
+ if atomic.LoadInt64(&newItems) < 10 {
+ return expErr
+ }
+
+ return nil
+ })
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, atomic.LoadInt64(&newItems), int64(9))
+ require.GreaterOrEqual(t, atomic.LoadInt64(&newItems), atomic.LoadInt64(&deleteItems))
+ err = p.Close(rootCtx)
+ require.NoError(t, err)
+ require.EqualValues(t, atomic.LoadInt64(&newItems), atomic.LoadInt64(&deleteItems))
+ }, xtest.StopAfter(5*time.Second))
+ })
+ })
+ t.Run("Stress", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ p := New[*testItem, testItem](rootCtx)
+ var wg sync.WaitGroup
+ wg.Add(DefaultLimit*2 + 1)
+ for range make([]struct{}, DefaultLimit*2) {
+ go func() {
+ defer wg.Done()
+ err := p.With(rootCtx, func(ctx context.Context, testItem *testItem) error {
+ return nil
+ })
+ if err != nil && !xerrors.Is(err, errClosedPool, context.Canceled) {
+ t.Failed()
+ }
+ }()
+ }
+ go func() {
+ defer wg.Done()
+ time.Sleep(time.Millisecond)
+ err := p.Close(rootCtx)
+ require.NoError(t, err)
+ }()
+ wg.Wait()
+ }, xtest.StopAfter(42*time.Second))
+ })
+}
+
+func TestSafeStatsRace(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ var (
+ wg sync.WaitGroup
+ s = &safeStats{}
+ )
+ wg.Add(10000)
+ for range make([]struct{}, 10000) {
+ go func() {
+ defer wg.Done()
+ require.NotPanics(t, func() {
+ switch rand.Int31n(4) { //nolint:gosec
+ case 0:
+ s.Index().Inc()
+ case 1:
+ s.InUse().Inc()
+ case 2:
+ s.Idle().Inc()
+ default:
+ s.Get()
+ }
+ })
+ }()
+ }
+ wg.Wait()
+ }, xtest.StopAfter(5*time.Second))
+}
diff --git a/internal/pool/stats/stats.go b/internal/pool/stats/stats.go
new file mode 100644
index 000000000..dff03eaeb
--- /dev/null
+++ b/internal/pool/stats/stats.go
@@ -0,0 +1,8 @@
+package stats
+
+type Stats struct {
+ Limit int
+ Index int
+ Idle int
+ InUse int
+}
diff --git a/internal/pool/trace.go b/internal/pool/trace.go
new file mode 100644
index 000000000..40adef256
--- /dev/null
+++ b/internal/pool/trace.go
@@ -0,0 +1,89 @@
+package pool
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool/stats"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+)
+
+type (
+ Trace struct {
+ OnNew func(*NewStartInfo) func(*NewDoneInfo)
+ OnClose func(*CloseStartInfo) func(*CloseDoneInfo)
+ OnTry func(*TryStartInfo) func(*TryDoneInfo)
+ OnWith func(*WithStartInfo) func(*WithDoneInfo)
+ OnPut func(*PutStartInfo) func(*PutDoneInfo)
+ OnGet func(*GetStartInfo) func(*GetDoneInfo)
+ OnChange func(ChangeInfo)
+ }
+ NewStartInfo struct {
+ // Context make available context in trace stack.Callerback function.
+ // Pointer to context provide replacement of context in trace stack.Callerback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside stack.Callerback function
+ Context *context.Context
+ Call stack.Caller
+ }
+ NewDoneInfo struct {
+ Limit int
+ }
+ CloseStartInfo struct {
+ // Context make available context in trace stack.Callerback function.
+ // Pointer to context provide replacement of context in trace stack.Callerback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside stack.Callerback function
+ Context *context.Context
+ Call stack.Caller
+ }
+ CloseDoneInfo struct {
+ Error error
+ }
+ TryStartInfo struct {
+ // Context make available context in trace stack.Callerback function.
+ // Pointer to context provide replacement of context in trace stack.Callerback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside stack.Callerback function
+ Context *context.Context
+ Call stack.Caller
+ }
+ TryDoneInfo struct {
+ Error error
+ }
+ WithStartInfo struct {
+ // Context make available context in trace stack.Callerback function.
+ // Pointer to context provide replacement of context in trace stack.Callerback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside stack.Callerback function
+ Context *context.Context
+ Call stack.Caller
+ }
+ WithDoneInfo struct {
+ Error error
+
+ Attempts int
+ }
+ PutStartInfo struct {
+ // Context make available context in trace stack.Callerback function.
+ // Pointer to context provide replacement of context in trace stack.Callerback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside stack.Callerback function
+ Context *context.Context
+ Call stack.Caller
+ }
+ PutDoneInfo struct {
+ Error error
+ }
+ GetStartInfo struct {
+ // Context make available context in trace stack.Callerback function.
+ // Pointer to context provide replacement of context in trace stack.Callerback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside stack.Callerback function
+ Context *context.Context
+ Call stack.Caller
+ }
+ GetDoneInfo struct {
+ Error error
+ }
+ ChangeInfo = stats.Stats
+)
diff --git a/internal/query/client.go b/internal/query/client.go
new file mode 100644
index 000000000..3019e83cd
--- /dev/null
+++ b/internal/query/client.go
@@ -0,0 +1,262 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ "google.golang.org/grpc"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool/stats"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/retry"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+//go:generate mockgen -destination grpc_client_mock_test.go -package query -write_package_comment=false github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1 QueryServiceClient,QueryService_AttachSessionClient,QueryService_ExecuteQueryClient
+
+type nodeChecker interface {
+ HasNode(id uint32) bool
+}
+
+type balancer interface {
+ grpc.ClientConnInterface
+ nodeChecker
+}
+
+var _ query.Client = (*Client)(nil)
+
+type Client struct {
+ config *config.Config
+ grpcClient Ydb_Query_V1.QueryServiceClient
+ pool *pool.Pool[*Session, Session]
+
+ done chan struct{}
+}
+
+func (c *Client) Stats() *stats.Stats {
+ s := c.pool.Stats()
+
+ return &s
+}
+
+func (c *Client) Close(ctx context.Context) error {
+ close(c.done)
+
+ err := c.pool.Close(ctx)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+}
+
+func do(
+ ctx context.Context,
+ pool *pool.Pool[*Session, Session],
+ op query.Operation,
+ t *trace.Query,
+ opts ...options.DoOption,
+) (attempts int, finalErr error) {
+ doOpts := options.ParseDoOpts(t, opts...)
+
+ err := pool.With(ctx, func(ctx context.Context, s *Session) error {
+ s.setStatus(statusInUse)
+
+ err := op(ctx, s)
+ if err != nil {
+ if !xerrors.IsRetryObjectValid(err) {
+ s.setStatus(statusError)
+ }
+
+ return xerrors.WithStackTrace(err)
+ }
+
+ s.setStatus(statusIdle)
+
+ return nil
+ }, append(doOpts.RetryOpts(), retry.WithTrace(&trace.Retry{
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
+ }
+ },
+ }))...)
+ if err != nil {
+ return attempts, xerrors.WithStackTrace(err)
+ }
+
+ return attempts, nil
+}
+
+func (c *Client) Do(ctx context.Context, op query.Operation, opts ...options.DoOption) error {
+ select {
+ case <-c.done:
+ return xerrors.WithStackTrace(errClosedClient)
+ default:
+ onDone := trace.QueryOnDo(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*Client).Do"),
+ )
+ attempts, err := do(ctx, c.pool, op, c.config.Trace(), opts...)
+ onDone(attempts, err)
+
+ return err
+ }
+}
+
+func doTx(
+ ctx context.Context,
+ pool *pool.Pool[*Session, Session],
+ op query.TxOperation,
+ t *trace.Query,
+ opts ...options.DoTxOption,
+) (attempts int, err error) {
+ doTxOpts := options.ParseDoTxOpts(t, opts...)
+
+ attempts, err = do(ctx, pool, func(ctx context.Context, s query.Session) (err error) {
+ tx, err := s.Begin(ctx, doTxOpts.TxSettings())
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+ err = op(ctx, tx)
+ if err != nil {
+ errRollback := tx.Rollback(ctx)
+ if errRollback != nil {
+ return xerrors.WithStackTrace(xerrors.Join(err, errRollback))
+ }
+
+ return xerrors.WithStackTrace(err)
+ }
+ err = tx.CommitTx(ctx)
+ if err != nil {
+ errRollback := tx.Rollback(ctx)
+ if errRollback != nil {
+ return xerrors.WithStackTrace(xerrors.Join(err, errRollback))
+ }
+
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+ }, t, doTxOpts.DoOpts()...)
+ if err != nil {
+ return attempts, xerrors.WithStackTrace(err)
+ }
+
+ return attempts, nil
+}
+
+func (c *Client) DoTx(ctx context.Context, op query.TxOperation, opts ...options.DoTxOption) (err error) {
+ select {
+ case <-c.done:
+ return xerrors.WithStackTrace(errClosedClient)
+ default:
+ onDone := trace.QueryOnDoTx(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*Client).DoTx"),
+ )
+ attempts, err := doTx(ctx, c.pool, op, c.config.Trace(), opts...)
+ onDone(attempts, err)
+
+ return err
+ }
+}
+
+func New(ctx context.Context, balancer balancer, cfg *config.Config) *Client {
+ onDone := trace.QueryOnNew(cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.New"),
+ )
+ defer onDone()
+
+ client := &Client{
+ config: cfg,
+ grpcClient: Ydb_Query_V1.NewQueryServiceClient(balancer),
+ done: make(chan struct{}),
+ }
+
+ client.pool = pool.New(ctx,
+ pool.WithLimit[*Session, Session](cfg.PoolLimit()),
+ pool.WithTrace[*Session, Session](poolTrace(cfg.Trace())),
+ pool.WithCreateItemTimeout[*Session, Session](cfg.SessionCreateTimeout()),
+ pool.WithCloseItemTimeout[*Session, Session](cfg.SessionDeleteTimeout()),
+ pool.WithCreateFunc(func(ctx context.Context) (_ *Session, err error) {
+ var (
+ createCtx context.Context
+ cancelCreate context.CancelFunc
+ )
+ if d := cfg.SessionCreateTimeout(); d > 0 {
+ createCtx, cancelCreate = xcontext.WithTimeout(ctx, d)
+ } else {
+ createCtx, cancelCreate = xcontext.WithCancel(ctx)
+ }
+ defer cancelCreate()
+
+ s, err := createSession(createCtx, client.grpcClient, cfg,
+ withSessionCheck(func(s *Session) bool {
+ return balancer.HasNode(uint32(s.nodeID))
+ }),
+ )
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return s, nil
+ }),
+ )
+
+ return client
+}
+
+func poolTrace(t *trace.Query) *pool.Trace {
+ return &pool.Trace{
+ OnNew: func(info *pool.NewStartInfo) func(*pool.NewDoneInfo) {
+ onDone := trace.QueryOnPoolNew(t, info.Context, info.Call)
+
+ return func(info *pool.NewDoneInfo) {
+ onDone(info.Limit)
+ }
+ },
+ OnClose: func(info *pool.CloseStartInfo) func(*pool.CloseDoneInfo) {
+ onDone := trace.QueryOnClose(t, info.Context, info.Call)
+
+ return func(info *pool.CloseDoneInfo) {
+ onDone(info.Error)
+ }
+ },
+ OnTry: func(info *pool.TryStartInfo) func(*pool.TryDoneInfo) {
+ onDone := trace.QueryOnPoolTry(t, info.Context, info.Call)
+
+ return func(info *pool.TryDoneInfo) {
+ onDone(info.Error)
+ }
+ },
+ OnWith: func(info *pool.WithStartInfo) func(*pool.WithDoneInfo) {
+ onDone := trace.QueryOnPoolWith(t, info.Context, info.Call)
+
+ return func(info *pool.WithDoneInfo) {
+ onDone(info.Error, info.Attempts)
+ }
+ },
+ OnPut: func(info *pool.PutStartInfo) func(*pool.PutDoneInfo) {
+ onDone := trace.QueryOnPoolPut(t, info.Context, info.Call)
+
+ return func(info *pool.PutDoneInfo) {
+ onDone(info.Error)
+ }
+ },
+ OnGet: func(info *pool.GetStartInfo) func(*pool.GetDoneInfo) {
+ onDone := trace.QueryOnPoolGet(t, info.Context, info.Call)
+
+ return func(info *pool.GetDoneInfo) {
+ onDone(info.Error)
+ }
+ },
+ OnChange: func(info pool.ChangeInfo) {
+ trace.QueryOnPoolChange(t, info.Limit, info.Index, info.Idle, info.InUse)
+ },
+ }
+}
diff --git a/internal/query/client_test.go b/internal/query/client_test.go
new file mode 100644
index 000000000..1b7260750
--- /dev/null
+++ b/internal/query/client_test.go
@@ -0,0 +1,258 @@
+package query
+
+import (
+ "context"
+ "errors"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "go.uber.org/mock/gomock"
+ grpcCodes "google.golang.org/grpc/codes"
+ grpcStatus "google.golang.org/grpc/status"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+func TestCreateSession(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ attachStream := NewMockQueryService_AttachSessionClient(ctrl)
+ attachStream.EXPECT().Recv().Return(&Ydb_Query.SessionState{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil).AnyTimes()
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ SessionId: "test",
+ }, nil)
+ service.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(attachStream, nil)
+ service.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.DeleteSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil)
+ attached := 0
+ s, err := createSession(ctx, service, config.New(config.WithTrace(
+ &trace.Query{
+ OnSessionAttach: func(info trace.QuerySessionAttachStartInfo) func(info trace.QuerySessionAttachDoneInfo) {
+ return func(info trace.QuerySessionAttachDoneInfo) {
+ if info.Error == nil {
+ attached++
+ }
+ }
+ },
+ OnSessionDelete: func(info trace.QuerySessionDeleteStartInfo) func(info trace.QuerySessionDeleteDoneInfo) {
+ attached--
+
+ return nil
+ },
+ },
+ )))
+ require.NoError(t, err)
+ require.EqualValues(t, "test", s.id)
+ require.EqualValues(t, 1, attached)
+ err = s.Close(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, attached)
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ t.Run("OnCall", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ _, err := createSession(ctx, service, config.New())
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("OnAttach", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ SessionId: "test",
+ }, nil)
+ service.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ service.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ _, err := createSession(ctx, service, config.New())
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("OnRecv", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ attachStream := NewMockQueryService_AttachSessionClient(ctrl)
+ attachStream.EXPECT().Recv().Return(nil, grpcStatus.Error(grpcCodes.Unavailable, "")).AnyTimes()
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ SessionId: "test",
+ }, nil)
+ service.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(attachStream, nil)
+ service.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.DeleteSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil)
+ _, err := createSession(ctx, service, config.New())
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ }, xtest.StopAfter(time.Second))
+ })
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ t.Run("OnCall", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ _, err := createSession(ctx, service, config.New())
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("OnRecv", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ attachStream := NewMockQueryService_AttachSessionClient(ctrl)
+ attachStream.EXPECT().Recv().Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CreateSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CreateSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ SessionId: "test",
+ }, nil)
+ service.EXPECT().AttachSession(gomock.Any(), gomock.Any()).Return(attachStream, nil)
+ service.EXPECT().DeleteSession(gomock.Any(), gomock.Any()).Return(&Ydb_Query.DeleteSessionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil)
+ _, err := createSession(ctx, service, config.New())
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ }, xtest.StopAfter(time.Second))
+ })
+ })
+}
+
+func newTestSession(id string) *Session {
+ return &Session{
+ id: id,
+ statusCode: statusIdle,
+ cfg: config.New(),
+ }
+}
+
+func newTestSessionWithClient(id string, client Ydb_Query_V1.QueryServiceClient) *Session {
+ return &Session{
+ id: id,
+ grpcClient: client,
+ statusCode: statusIdle,
+ cfg: config.New(),
+ }
+}
+
+func testPool(
+ ctx context.Context,
+ createSession func(ctx context.Context) (*Session, error),
+) *pool.Pool[*Session, Session] {
+ return pool.New[*Session, Session](ctx,
+ pool.WithLimit[*Session, Session](1),
+ pool.WithCreateFunc(createSession),
+ )
+}
+
+func TestDo(t *testing.T) {
+ ctx := xtest.Context(t)
+ t.Run("HappyWay", func(t *testing.T) {
+ attempts, err := do(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
+ return newTestSession("123"), nil
+ }), func(ctx context.Context, s query.Session) error {
+ return nil
+ }, &trace.Query{})
+ require.NoError(t, err)
+ require.EqualValues(t, 1, attempts)
+ })
+ t.Run("RetryableError", func(t *testing.T) {
+ counter := 0
+ attempts, err := do(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
+ return newTestSession("123"), nil
+ }), func(ctx context.Context, s query.Session) error {
+ counter++
+ if counter < 10 {
+ return xerrors.Retryable(errors.New(""))
+ }
+
+ return nil
+ }, &trace.Query{})
+ require.NoError(t, err)
+ require.EqualValues(t, 10, attempts)
+ require.Equal(t, 10, counter)
+ })
+}
+
+func TestDoTx(t *testing.T) {
+ ctx := xtest.Context(t)
+ t.Run("HappyWay", func(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ client := NewMockQueryServiceClient(ctrl)
+ client.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.BeginTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil)
+ client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CommitTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil)
+ attempts, err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
+ return newTestSessionWithClient("123", client), nil
+ }), func(ctx context.Context, tx query.TxActor) error {
+ return nil
+ }, &trace.Query{})
+ require.NoError(t, err)
+ require.EqualValues(t, 1, attempts)
+ })
+ t.Run("RetryableError", func(t *testing.T) {
+ counter := 0
+ ctrl := gomock.NewController(t)
+ client := NewMockQueryServiceClient(ctrl)
+ client.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.BeginTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil).AnyTimes()
+ client.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.RollbackTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil).AnyTimes()
+ client.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.CommitTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil).AnyTimes()
+ attempts, err := doTx(ctx, testPool(ctx, func(ctx context.Context) (*Session, error) {
+ return newTestSessionWithClient("123", client), nil
+ }), func(ctx context.Context, tx query.TxActor) error {
+ counter++
+ if counter < 10 {
+ return xerrors.Retryable(errors.New(""))
+ }
+
+ return nil
+ }, &trace.Query{})
+ require.NoError(t, err)
+ require.EqualValues(t, 10, attempts)
+ require.Equal(t, 10, counter)
+ })
+}
diff --git a/internal/query/config/config.go b/internal/query/config/config.go
new file mode 100644
index 000000000..7adb08242
--- /dev/null
+++ b/internal/query/config/config.go
@@ -0,0 +1,70 @@
+package config
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+const (
+ DefaultSessionDeleteTimeout = 500 * time.Millisecond
+ DefaultSessionCreateTimeout = 500 * time.Millisecond
+ DefaultPoolMaxSize = pool.DefaultLimit
+)
+
+type Config struct {
+ config.Common
+
+ poolLimit int
+
+ sessionCreateTimeout time.Duration
+ sessionDeleteTimeout time.Duration
+
+ trace *trace.Query
+}
+
+func New(opts ...Option) *Config {
+ c := defaults()
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
+ }
+ }
+
+ return c
+}
+
+func defaults() *Config {
+ return &Config{
+ poolLimit: DefaultPoolMaxSize,
+ sessionCreateTimeout: DefaultSessionCreateTimeout,
+ sessionDeleteTimeout: DefaultSessionDeleteTimeout,
+ trace: &trace.Query{},
+ }
+}
+
+// Trace defines trace over table client calls
+func (c *Config) Trace() *trace.Query {
+ return c.trace
+}
+
+// PoolLimit is an upper bound of pooled sessions.
+// If PoolLimit is less than or equal to zero then the
+// DefaultPoolMaxSize variable is used as a pool limit.
+func (c *Config) PoolLimit() int {
+ return c.poolLimit
+}
+
+// SessionCreateTimeout limits maximum time spent on Create session request
+func (c *Config) SessionCreateTimeout() time.Duration {
+ return c.sessionCreateTimeout
+}
+
+// SessionDeleteTimeout limits maximum time spent on Delete request
+//
+// If SessionDeleteTimeout is less than or equal to zero then the DefaultSessionDeleteTimeout is used.
+func (c *Config) SessionDeleteTimeout() time.Duration {
+ return c.sessionDeleteTimeout
+}
diff --git a/internal/query/config/options.go b/internal/query/config/options.go
new file mode 100644
index 000000000..2b30c5be2
--- /dev/null
+++ b/internal/query/config/options.go
@@ -0,0 +1,57 @@
+package config
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+type Option func(*Config)
+
+// With applies common configuration params
+func With(config config.Common) Option {
+ return func(c *Config) {
+ c.Common = config
+ }
+}
+
+// WithTrace appends table trace to early defined traces
+func WithTrace(trace *trace.Query, opts ...trace.QueryComposeOption) Option {
+ return func(c *Config) {
+ c.trace = c.trace.Compose(trace, opts...)
+ }
+}
+
+// WithPoolLimit defines upper bound of pooled sessions.
+// If poolLimit is less than or equal to zero then the
+// DefaultPoolMaxSize variable is used as a poolLimit.
+func WithPoolLimit(size int) Option {
+ return func(c *Config) {
+ if size > 0 {
+ c.poolLimit = size
+ }
+ }
+}
+
+// WithSessionCreateTimeout limits maximum time spent on Create session request
+// If sessionCreateTimeout is less than or equal to zero then no used timeout on create session request
+func WithSessionCreateTimeout(createSessionTimeout time.Duration) Option {
+ return func(c *Config) {
+ if createSessionTimeout > 0 {
+ c.sessionCreateTimeout = createSessionTimeout
+ } else {
+ c.sessionCreateTimeout = 0
+ }
+ }
+}
+
+// WithSessionDeleteTimeout limits maximum time spent on Delete request
+// If sessionDeleteTimeout is less than or equal to zero then the DefaultSessionDeleteTimeout is used.
+func WithSessionDeleteTimeout(deleteTimeout time.Duration) Option {
+ return func(c *Config) {
+ if deleteTimeout > 0 {
+ c.sessionDeleteTimeout = deleteTimeout
+ }
+ }
+}
diff --git a/internal/query/errors.go b/internal/query/errors.go
new file mode 100644
index 000000000..923ef8ed8
--- /dev/null
+++ b/internal/query/errors.go
@@ -0,0 +1,13 @@
+package query
+
+import (
+ "errors"
+)
+
+var (
+ ErrNotImplemented = errors.New("not implemented yet")
+ errWrongNextResultSetIndex = errors.New("wrong result set index")
+ errClosedResult = errors.New("result closed early")
+ errClosedClient = errors.New("query client closed early")
+ errWrongResultSetIndex = errors.New("critical violation of the logic - wrong result set index")
+)
diff --git a/internal/query/execute_query.go b/internal/query/execute_query.go
new file mode 100644
index 000000000..bf2fef314
--- /dev/null
+++ b/internal/query/execute_query.go
@@ -0,0 +1,82 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "google.golang.org/grpc"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+)
+
+type executeConfig interface {
+ ExecMode() options.ExecMode
+ StatsMode() options.StatsMode
+ TxControl() *query.TransactionControl
+ Syntax() options.Syntax
+ Params() *params.Parameters
+ CallOptions() []grpc.CallOption
+}
+
+func executeQueryRequest(a *allocator.Allocator, sessionID, q string, cfg executeConfig) (
+ *Ydb_Query.ExecuteQueryRequest,
+ []grpc.CallOption,
+) {
+ request := a.QueryExecuteQueryRequest()
+
+ request.SessionId = sessionID
+ request.ExecMode = Ydb_Query.ExecMode(cfg.ExecMode())
+ request.TxControl = cfg.TxControl().ToYDB(a)
+ request.Query = queryFromText(a, q, Ydb_Query.Syntax(cfg.Syntax()))
+ request.Parameters = cfg.Params().ToYDB(a)
+ request.StatsMode = Ydb_Query.StatsMode(cfg.StatsMode())
+ request.ConcurrentResultSets = false
+
+ return request, cfg.CallOptions()
+}
+
+func queryFromText(
+ a *allocator.Allocator, q string, syntax Ydb_Query.Syntax,
+) *Ydb_Query.ExecuteQueryRequest_QueryContent {
+ content := a.QueryExecuteQueryRequestQueryContent()
+ content.QueryContent = a.QueryQueryContent()
+ content.QueryContent.Syntax = syntax
+ content.QueryContent.Text = q
+
+ return content
+}
+
+func execute(ctx context.Context, s *Session, c Ydb_Query_V1.QueryServiceClient, q string, cfg executeConfig) (
+ _ *transaction, _ *result, finalErr error,
+) {
+ a := allocator.New()
+ defer a.Free()
+
+ request, callOptions := executeQueryRequest(a, s.id, q, cfg)
+
+ executeCtx, cancelExecute := xcontext.WithCancel(xcontext.ValueOnly(ctx))
+
+ stream, err := c.ExecuteQuery(executeCtx, request, callOptions...)
+ if err != nil {
+ return nil, nil, xerrors.WithStackTrace(err)
+ }
+
+ r, txID, err := newResult(ctx, stream, s.cfg.Trace(), cancelExecute)
+ if err != nil {
+ cancelExecute()
+
+ return nil, nil, xerrors.WithStackTrace(err)
+ }
+
+ if txID == "" {
+ return nil, r, nil
+ }
+
+ return newTransaction(txID, s), r, nil
+}
diff --git a/internal/query/execute_query_test.go b/internal/query/execute_query_test.go
new file mode 100644
index 000000000..c3c14d754
--- /dev/null
+++ b/internal/query/execute_query_test.go
@@ -0,0 +1,1012 @@
+package query
+
+import (
+ "context"
+ "io"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "go.uber.org/mock/gomock"
+ "google.golang.org/grpc"
+ grpcCodes "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/metadata"
+ grpcStatus "google.golang.org/grpc/status"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+)
+
+func TestExecute(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ TxMeta: &Ydb_Query.TransactionMeta{
+ Id: "456",
+ },
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 1,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "d",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "e",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 1,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 2,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "d",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "e",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 2,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, io.EOF)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
+ tx, r, err := execute(ctx, newTestSession("123"), service, "", options.ExecuteSettings())
+ require.NoError(t, err)
+ defer r.Close(ctx)
+ require.EqualValues(t, "456", tx.id)
+ require.EqualValues(t, "123", tx.s.id)
+ require.EqualValues(t, -1, r.resultSetIndex)
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=5)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=6)")
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ }
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.index)
+ }
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=5)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=6)")
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ }
+ {
+ t.Log("close result")
+ r.Close(context.Background())
+ }
+ {
+ t.Log("nextResultSet")
+ _, err := r.nextResultSet(context.Background())
+ require.ErrorIs(t, err, errClosedResult)
+ }
+ t.Log("check final error")
+ require.NoError(t, r.Err())
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ t.Run("OnCall", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ t.Log("execute")
+ _, _, err := execute(ctx, newTestSession("123"), service, "", options.ExecuteSettings())
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ })
+ t.Run("OnStream", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ TxMeta: &Ydb_Query.TransactionMeta{
+ Id: "456",
+ },
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
+ t.Log("execute")
+ tx, r, err := execute(ctx, newTestSession("123"), service, "", options.ExecuteSettings())
+ require.NoError(t, err)
+ defer r.Close(ctx)
+ require.EqualValues(t, "456", tx.id)
+ require.EqualValues(t, "123", tx.s.id)
+ require.EqualValues(t, -1, r.resultSetIndex)
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=5)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=6)")
+ _, err := rs.nextRow(ctx)
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ }
+ }
+ t.Log("check final error")
+ require.Error(t, r.Err())
+ require.True(t, xerrors.IsTransportError(r.Err(), grpcCodes.Unavailable))
+ })
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ t.Run("OnCall", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(nil, xerrors.Operation(xerrors.WithStatusCode(
+ Ydb.StatusIds_UNAVAILABLE,
+ )))
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
+ t.Log("execute")
+ _, _, err := execute(ctx, newTestSession("123"), service, "", options.ExecuteSettings())
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ })
+ t.Run("OnStream", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ TxMeta: &Ydb_Query.TransactionMeta{
+ Id: "456",
+ },
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, xerrors.Operation(xerrors.WithStatusCode(
+ Ydb.StatusIds_UNAVAILABLE,
+ )))
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().ExecuteQuery(gomock.Any(), gomock.Any()).Return(stream, nil)
+ t.Log("execute")
+ tx, r, err := execute(ctx, newTestSession("123"), service, "", options.ExecuteSettings())
+ require.NoError(t, err)
+ defer r.Close(ctx)
+ require.EqualValues(t, "456", tx.id)
+ require.EqualValues(t, "123", tx.s.id)
+ require.EqualValues(t, -1, r.resultSetIndex)
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ }
+ }
+ t.Log("check final error")
+ require.Error(t, r.Err())
+ require.True(t, xerrors.IsOperationError(r.Err(), Ydb.StatusIds_UNAVAILABLE))
+ })
+ })
+}
+
+func TestExecuteQueryRequest(t *testing.T) {
+ a := allocator.New()
+ for _, tt := range []struct {
+ name string
+ opts []options.ExecuteOption
+ request *Ydb_Query.ExecuteQueryRequest
+ callOptions []grpc.CallOption
+ }{
+ {
+ name: "WithoutOptions",
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithoutOptions",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithoutOptions",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithTxControl",
+ opts: []options.ExecuteOption{
+ options.WithTxControl(query.SerializableReadWriteTxControl(query.CommitTx())),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithTxControl",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ TxControl: &Ydb_Query.TransactionControl{
+ TxSelector: &Ydb_Query.TransactionControl_BeginTx{
+ BeginTx: &Ydb_Query.TransactionSettings{
+ TxMode: &Ydb_Query.TransactionSettings_SerializableReadWrite{
+ SerializableReadWrite: &Ydb_Query.SerializableModeSettings{},
+ },
+ },
+ },
+ CommitTx: true,
+ },
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithTxControl",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithParams",
+ opts: []options.ExecuteOption{
+ options.WithParameters(
+ params.Builder{}.
+ Param("$a").Text("A").
+ Param("$b").Text("B").
+ Param("$c").Text("C").
+ Build(),
+ ),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithParams",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithParams",
+ },
+ },
+ Parameters: map[string]*Ydb.TypedValue{
+ "$a": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "A",
+ },
+ },
+ },
+ "$b": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "B",
+ },
+ },
+ },
+ "$c": {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ Value: &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: "C",
+ },
+ },
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithExplain",
+ opts: []options.ExecuteOption{
+ options.WithExecMode(options.ExecModeExplain),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithExplain",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXPLAIN,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithExplain",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithValidate",
+ opts: []options.ExecuteOption{
+ options.WithExecMode(options.ExecModeValidate),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithValidate",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_VALIDATE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithValidate",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithValidate",
+ opts: []options.ExecuteOption{
+ options.WithExecMode(options.ExecModeParse),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithValidate",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_PARSE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithValidate",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithStatsFull",
+ opts: []options.ExecuteOption{
+ options.WithStatsMode(options.StatsModeFull),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithStatsFull",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithStatsFull",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_FULL,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithStatsBasic",
+ opts: []options.ExecuteOption{
+ options.WithStatsMode(options.StatsModeBasic),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithStatsBasic",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithStatsBasic",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_BASIC,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithStatsProfile",
+ opts: []options.ExecuteOption{
+ options.WithStatsMode(options.StatsModeProfile),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithStatsProfile",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithStatsProfile",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_PROFILE,
+ ConcurrentResultSets: false,
+ },
+ },
+ {
+ name: "WithGrpcCallOptions",
+ opts: []options.ExecuteOption{
+ options.WithCallOptions(grpc.Header(&metadata.MD{
+ "ext-header": []string{"test"},
+ })),
+ },
+ request: &Ydb_Query.ExecuteQueryRequest{
+ SessionId: "WithGrpcCallOptions",
+ ExecMode: Ydb_Query.ExecMode_EXEC_MODE_EXECUTE,
+ Query: &Ydb_Query.ExecuteQueryRequest_QueryContent{
+ QueryContent: &Ydb_Query.QueryContent{
+ Syntax: Ydb_Query.Syntax_SYNTAX_YQL_V1,
+ Text: "WithGrpcCallOptions",
+ },
+ },
+ StatsMode: Ydb_Query.StatsMode_STATS_MODE_NONE,
+ ConcurrentResultSets: false,
+ },
+ callOptions: []grpc.CallOption{
+ grpc.Header(&metadata.MD{
+ "ext-header": []string{"test"},
+ }),
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ request, callOptions := executeQueryRequest(a, tt.name, tt.name, options.ExecuteSettings(tt.opts...))
+ require.Equal(t, request.String(), tt.request.String())
+ require.Equal(t, tt.callOptions, callOptions)
+ })
+ }
+}
diff --git a/internal/query/grpc_client_mock_test.go b/internal/query/grpc_client_mock_test.go
new file mode 100644
index 000000000..d81f0cfab
--- /dev/null
+++ b/internal/query/grpc_client_mock_test.go
@@ -0,0 +1,468 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1 (interfaces: QueryServiceClient,QueryService_AttachSessionClient,QueryService_ExecuteQueryClient)
+//
+// Generated by this command:
+//
+// mockgen -destination grpc_client_mock_test.go -package query -write_package_comment=false github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1 QueryServiceClient,QueryService_AttachSessionClient,QueryService_ExecuteQueryClient
+package query
+
+import (
+ context "context"
+ reflect "reflect"
+
+ Ydb_Query_V1 "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ Ydb_Operations "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations"
+ Ydb_Query "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ gomock "go.uber.org/mock/gomock"
+ grpc "google.golang.org/grpc"
+ metadata "google.golang.org/grpc/metadata"
+)
+
+// MockQueryServiceClient is a mock of QueryServiceClient interface.
+type MockQueryServiceClient struct {
+ ctrl *gomock.Controller
+ recorder *MockQueryServiceClientMockRecorder
+}
+
+// MockQueryServiceClientMockRecorder is the mock recorder for MockQueryServiceClient.
+type MockQueryServiceClientMockRecorder struct {
+ mock *MockQueryServiceClient
+}
+
+// NewMockQueryServiceClient creates a new mock instance.
+func NewMockQueryServiceClient(ctrl *gomock.Controller) *MockQueryServiceClient {
+ mock := &MockQueryServiceClient{ctrl: ctrl}
+ mock.recorder = &MockQueryServiceClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockQueryServiceClient) EXPECT() *MockQueryServiceClientMockRecorder {
+ return m.recorder
+}
+
+// AttachSession mocks base method.
+func (m *MockQueryServiceClient) AttachSession(arg0 context.Context, arg1 *Ydb_Query.AttachSessionRequest, arg2 ...grpc.CallOption) (Ydb_Query_V1.QueryService_AttachSessionClient, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "AttachSession", varargs...)
+ ret0, _ := ret[0].(Ydb_Query_V1.QueryService_AttachSessionClient)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// AttachSession indicates an expected call of AttachSession.
+func (mr *MockQueryServiceClientMockRecorder) AttachSession(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachSession", reflect.TypeOf((*MockQueryServiceClient)(nil).AttachSession), varargs...)
+}
+
+// BeginTransaction mocks base method.
+func (m *MockQueryServiceClient) BeginTransaction(arg0 context.Context, arg1 *Ydb_Query.BeginTransactionRequest, arg2 ...grpc.CallOption) (*Ydb_Query.BeginTransactionResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "BeginTransaction", varargs...)
+ ret0, _ := ret[0].(*Ydb_Query.BeginTransactionResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// BeginTransaction indicates an expected call of BeginTransaction.
+func (mr *MockQueryServiceClientMockRecorder) BeginTransaction(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTransaction", reflect.TypeOf((*MockQueryServiceClient)(nil).BeginTransaction), varargs...)
+}
+
+// CommitTransaction mocks base method.
+func (m *MockQueryServiceClient) CommitTransaction(arg0 context.Context, arg1 *Ydb_Query.CommitTransactionRequest, arg2 ...grpc.CallOption) (*Ydb_Query.CommitTransactionResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "CommitTransaction", varargs...)
+ ret0, _ := ret[0].(*Ydb_Query.CommitTransactionResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// CommitTransaction indicates an expected call of CommitTransaction.
+func (mr *MockQueryServiceClientMockRecorder) CommitTransaction(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitTransaction", reflect.TypeOf((*MockQueryServiceClient)(nil).CommitTransaction), varargs...)
+}
+
+// CreateSession mocks base method.
+func (m *MockQueryServiceClient) CreateSession(arg0 context.Context, arg1 *Ydb_Query.CreateSessionRequest, arg2 ...grpc.CallOption) (*Ydb_Query.CreateSessionResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "CreateSession", varargs...)
+ ret0, _ := ret[0].(*Ydb_Query.CreateSessionResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// CreateSession indicates an expected call of CreateSession.
+func (mr *MockQueryServiceClientMockRecorder) CreateSession(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockQueryServiceClient)(nil).CreateSession), varargs...)
+}
+
+// DeleteSession mocks base method.
+func (m *MockQueryServiceClient) DeleteSession(arg0 context.Context, arg1 *Ydb_Query.DeleteSessionRequest, arg2 ...grpc.CallOption) (*Ydb_Query.DeleteSessionResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "DeleteSession", varargs...)
+ ret0, _ := ret[0].(*Ydb_Query.DeleteSessionResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// DeleteSession indicates an expected call of DeleteSession.
+func (mr *MockQueryServiceClientMockRecorder) DeleteSession(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSession", reflect.TypeOf((*MockQueryServiceClient)(nil).DeleteSession), varargs...)
+}
+
+// ExecuteQuery mocks base method.
+func (m *MockQueryServiceClient) ExecuteQuery(arg0 context.Context, arg1 *Ydb_Query.ExecuteQueryRequest, arg2 ...grpc.CallOption) (Ydb_Query_V1.QueryService_ExecuteQueryClient, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "ExecuteQuery", varargs...)
+ ret0, _ := ret[0].(Ydb_Query_V1.QueryService_ExecuteQueryClient)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ExecuteQuery indicates an expected call of ExecuteQuery.
+func (mr *MockQueryServiceClientMockRecorder) ExecuteQuery(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteQuery", reflect.TypeOf((*MockQueryServiceClient)(nil).ExecuteQuery), varargs...)
+}
+
+// ExecuteScript mocks base method.
+func (m *MockQueryServiceClient) ExecuteScript(arg0 context.Context, arg1 *Ydb_Query.ExecuteScriptRequest, arg2 ...grpc.CallOption) (*Ydb_Operations.Operation, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "ExecuteScript", varargs...)
+ ret0, _ := ret[0].(*Ydb_Operations.Operation)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ExecuteScript indicates an expected call of ExecuteScript.
+func (mr *MockQueryServiceClientMockRecorder) ExecuteScript(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteScript", reflect.TypeOf((*MockQueryServiceClient)(nil).ExecuteScript), varargs...)
+}
+
+// FetchScriptResults mocks base method.
+func (m *MockQueryServiceClient) FetchScriptResults(arg0 context.Context, arg1 *Ydb_Query.FetchScriptResultsRequest, arg2 ...grpc.CallOption) (*Ydb_Query.FetchScriptResultsResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "FetchScriptResults", varargs...)
+ ret0, _ := ret[0].(*Ydb_Query.FetchScriptResultsResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// FetchScriptResults indicates an expected call of FetchScriptResults.
+func (mr *MockQueryServiceClientMockRecorder) FetchScriptResults(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchScriptResults", reflect.TypeOf((*MockQueryServiceClient)(nil).FetchScriptResults), varargs...)
+}
+
+// RollbackTransaction mocks base method.
+func (m *MockQueryServiceClient) RollbackTransaction(arg0 context.Context, arg1 *Ydb_Query.RollbackTransactionRequest, arg2 ...grpc.CallOption) (*Ydb_Query.RollbackTransactionResponse, error) {
+ m.ctrl.T.Helper()
+ varargs := []any{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "RollbackTransaction", varargs...)
+ ret0, _ := ret[0].(*Ydb_Query.RollbackTransactionResponse)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// RollbackTransaction indicates an expected call of RollbackTransaction.
+func (mr *MockQueryServiceClientMockRecorder) RollbackTransaction(arg0, arg1 any, arg2 ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackTransaction", reflect.TypeOf((*MockQueryServiceClient)(nil).RollbackTransaction), varargs...)
+}
+
+// MockQueryService_AttachSessionClient is a mock of QueryService_AttachSessionClient interface.
+type MockQueryService_AttachSessionClient struct {
+ ctrl *gomock.Controller
+ recorder *MockQueryService_AttachSessionClientMockRecorder
+}
+
+// MockQueryService_AttachSessionClientMockRecorder is the mock recorder for MockQueryService_AttachSessionClient.
+type MockQueryService_AttachSessionClientMockRecorder struct {
+ mock *MockQueryService_AttachSessionClient
+}
+
+// NewMockQueryService_AttachSessionClient creates a new mock instance.
+func NewMockQueryService_AttachSessionClient(ctrl *gomock.Controller) *MockQueryService_AttachSessionClient {
+ mock := &MockQueryService_AttachSessionClient{ctrl: ctrl}
+ mock.recorder = &MockQueryService_AttachSessionClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockQueryService_AttachSessionClient) EXPECT() *MockQueryService_AttachSessionClientMockRecorder {
+ return m.recorder
+}
+
+// CloseSend mocks base method.
+func (m *MockQueryService_AttachSessionClient) CloseSend() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CloseSend")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// CloseSend indicates an expected call of CloseSend.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) CloseSend() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).CloseSend))
+}
+
+// Context mocks base method.
+func (m *MockQueryService_AttachSessionClient) Context() context.Context {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Context")
+ ret0, _ := ret[0].(context.Context)
+ return ret0
+}
+
+// Context indicates an expected call of Context.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) Context() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).Context))
+}
+
+// Header mocks base method.
+func (m *MockQueryService_AttachSessionClient) Header() (metadata.MD, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Header")
+ ret0, _ := ret[0].(metadata.MD)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Header indicates an expected call of Header.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) Header() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).Header))
+}
+
+// Recv mocks base method.
+func (m *MockQueryService_AttachSessionClient) Recv() (*Ydb_Query.SessionState, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Recv")
+ ret0, _ := ret[0].(*Ydb_Query.SessionState)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Recv indicates an expected call of Recv.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) Recv() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).Recv))
+}
+
+// RecvMsg mocks base method.
+func (m *MockQueryService_AttachSessionClient) RecvMsg(arg0 any) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "RecvMsg", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// RecvMsg indicates an expected call of RecvMsg.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) RecvMsg(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).RecvMsg), arg0)
+}
+
+// SendMsg mocks base method.
+func (m *MockQueryService_AttachSessionClient) SendMsg(arg0 any) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SendMsg", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendMsg indicates an expected call of SendMsg.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) SendMsg(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).SendMsg), arg0)
+}
+
+// Trailer mocks base method.
+func (m *MockQueryService_AttachSessionClient) Trailer() metadata.MD {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Trailer")
+ ret0, _ := ret[0].(metadata.MD)
+ return ret0
+}
+
+// Trailer indicates an expected call of Trailer.
+func (mr *MockQueryService_AttachSessionClientMockRecorder) Trailer() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockQueryService_AttachSessionClient)(nil).Trailer))
+}
+
+// MockQueryService_ExecuteQueryClient is a mock of QueryService_ExecuteQueryClient interface.
+type MockQueryService_ExecuteQueryClient struct {
+ ctrl *gomock.Controller
+ recorder *MockQueryService_ExecuteQueryClientMockRecorder
+}
+
+// MockQueryService_ExecuteQueryClientMockRecorder is the mock recorder for MockQueryService_ExecuteQueryClient.
+type MockQueryService_ExecuteQueryClientMockRecorder struct {
+ mock *MockQueryService_ExecuteQueryClient
+}
+
+// NewMockQueryService_ExecuteQueryClient creates a new mock instance.
+func NewMockQueryService_ExecuteQueryClient(ctrl *gomock.Controller) *MockQueryService_ExecuteQueryClient {
+ mock := &MockQueryService_ExecuteQueryClient{ctrl: ctrl}
+ mock.recorder = &MockQueryService_ExecuteQueryClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockQueryService_ExecuteQueryClient) EXPECT() *MockQueryService_ExecuteQueryClientMockRecorder {
+ return m.recorder
+}
+
+// CloseSend mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) CloseSend() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CloseSend")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// CloseSend indicates an expected call of CloseSend.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) CloseSend() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).CloseSend))
+}
+
+// Context mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) Context() context.Context {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Context")
+ ret0, _ := ret[0].(context.Context)
+ return ret0
+}
+
+// Context indicates an expected call of Context.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) Context() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).Context))
+}
+
+// Header mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) Header() (metadata.MD, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Header")
+ ret0, _ := ret[0].(metadata.MD)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Header indicates an expected call of Header.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) Header() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).Header))
+}
+
+// Recv mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) Recv() (*Ydb_Query.ExecuteQueryResponsePart, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Recv")
+ ret0, _ := ret[0].(*Ydb_Query.ExecuteQueryResponsePart)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Recv indicates an expected call of Recv.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) Recv() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).Recv))
+}
+
+// RecvMsg mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) RecvMsg(arg0 any) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "RecvMsg", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// RecvMsg indicates an expected call of RecvMsg.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) RecvMsg(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).RecvMsg), arg0)
+}
+
+// SendMsg mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) SendMsg(arg0 any) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SendMsg", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SendMsg indicates an expected call of SendMsg.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) SendMsg(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).SendMsg), arg0)
+}
+
+// Trailer mocks base method.
+func (m *MockQueryService_ExecuteQueryClient) Trailer() metadata.MD {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Trailer")
+ ret0, _ := ret[0].(metadata.MD)
+ return ret0
+}
+
+// Trailer indicates an expected call of Trailer.
+func (mr *MockQueryService_ExecuteQueryClientMockRecorder) Trailer() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockQueryService_ExecuteQueryClient)(nil).Trailer))
+}
diff --git a/internal/query/options/execute.go b/internal/query/options/execute.go
new file mode 100644
index 000000000..6a306c26d
--- /dev/null
+++ b/internal/query/options/execute.go
@@ -0,0 +1,224 @@
+package options
+
+import (
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "google.golang.org/grpc"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/tx"
+)
+
+type (
+ Syntax Ydb_Query.Syntax
+ ExecMode Ydb_Query.ExecMode
+ StatsMode Ydb_Query.StatsMode
+ CallOptions []grpc.CallOption
+ commonExecuteSettings struct {
+ syntax Syntax
+ params params.Parameters
+ execMode ExecMode
+ statsMode StatsMode
+ callOptions []grpc.CallOption
+ }
+ Execute struct {
+ commonExecuteSettings
+
+ txControl *tx.Control
+ }
+ ExecuteOption interface {
+ applyExecuteOption(s *Execute)
+ }
+ txExecuteSettings struct {
+ ExecuteSettings *Execute
+
+ commitTx bool
+ }
+ TxExecuteOption interface {
+ applyTxExecuteOption(s *txExecuteSettings)
+ }
+ txCommitOption struct{}
+ ParametersOption params.Parameters
+ TxControlOption struct {
+ txControl *tx.Control
+ }
+)
+
+func (opt TxControlOption) applyExecuteOption(s *Execute) {
+ s.txControl = opt.txControl
+}
+
+func (t txCommitOption) applyTxExecuteOption(s *txExecuteSettings) {
+ s.commitTx = true
+}
+
+func (syntax Syntax) applyTxExecuteOption(s *txExecuteSettings) {
+ syntax.applyExecuteOption(s.ExecuteSettings)
+}
+
+func (syntax Syntax) applyExecuteOption(s *Execute) {
+ s.syntax = syntax
+}
+
+const (
+ SyntaxYQL = Syntax(Ydb_Query.Syntax_SYNTAX_YQL_V1)
+ SyntaxPostgreSQL = Syntax(Ydb_Query.Syntax_SYNTAX_PG)
+)
+
+func (params ParametersOption) applyTxExecuteOption(s *txExecuteSettings) {
+ params.applyExecuteOption(s.ExecuteSettings)
+}
+
+func (params ParametersOption) applyExecuteOption(s *Execute) {
+ s.params = append(s.params, params...)
+}
+
+func (opts CallOptions) applyExecuteOption(s *Execute) {
+ s.callOptions = append(s.callOptions, opts...)
+}
+
+func (opts CallOptions) applyTxExecuteOption(s *txExecuteSettings) {
+ opts.applyExecuteOption(s.ExecuteSettings)
+}
+
+func (mode StatsMode) applyTxExecuteOption(s *txExecuteSettings) {
+ mode.applyExecuteOption(s.ExecuteSettings)
+}
+
+func (mode StatsMode) applyExecuteOption(s *Execute) {
+ s.statsMode = mode
+}
+
+func (mode ExecMode) applyTxExecuteOption(s *txExecuteSettings) {
+ mode.applyExecuteOption(s.ExecuteSettings)
+}
+
+func (mode ExecMode) applyExecuteOption(s *Execute) {
+ s.execMode = mode
+}
+
+const (
+ ExecModeParse = ExecMode(Ydb_Query.ExecMode_EXEC_MODE_PARSE)
+ ExecModeValidate = ExecMode(Ydb_Query.ExecMode_EXEC_MODE_VALIDATE)
+ ExecModeExplain = ExecMode(Ydb_Query.ExecMode_EXEC_MODE_EXPLAIN)
+ ExecModeExecute = ExecMode(Ydb_Query.ExecMode_EXEC_MODE_EXECUTE)
+)
+
+const (
+ StatsModeBasic = StatsMode(Ydb_Query.StatsMode_STATS_MODE_BASIC)
+ StatsModeNone = StatsMode(Ydb_Query.StatsMode_STATS_MODE_NONE)
+ StatsModeFull = StatsMode(Ydb_Query.StatsMode_STATS_MODE_FULL)
+ StatsModeProfile = StatsMode(Ydb_Query.StatsMode_STATS_MODE_PROFILE)
+)
+
+func defaultCommonExecuteSettings() commonExecuteSettings {
+ return commonExecuteSettings{
+ syntax: SyntaxYQL,
+ execMode: ExecModeExecute,
+ statsMode: StatsModeNone,
+ }
+}
+
+func ExecuteSettings(opts ...ExecuteOption) (settings *Execute) {
+ settings = &Execute{
+ commonExecuteSettings: defaultCommonExecuteSettings(),
+ }
+ settings.commonExecuteSettings = defaultCommonExecuteSettings()
+ settings.txControl = tx.DefaultTxControl()
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyExecuteOption(settings)
+ }
+ }
+
+ return settings
+}
+
+func (s *Execute) TxControl() *tx.Control {
+ return s.txControl
+}
+
+func (s *Execute) SetTxControl(ctrl *tx.Control) {
+ s.txControl = ctrl
+}
+
+func (s *commonExecuteSettings) CallOptions() []grpc.CallOption {
+ return s.callOptions
+}
+
+func (s *commonExecuteSettings) Syntax() Syntax {
+ return s.syntax
+}
+
+func (s *commonExecuteSettings) ExecMode() ExecMode {
+ return s.execMode
+}
+
+func (s *commonExecuteSettings) StatsMode() StatsMode {
+ return s.statsMode
+}
+
+func (s *commonExecuteSettings) Params() *params.Parameters {
+ if len(s.params) == 0 {
+ return nil
+ }
+
+ return &s.params
+}
+
+func TxExecuteSettings(id string, opts ...TxExecuteOption) (settings *txExecuteSettings) {
+ settings = &txExecuteSettings{
+ ExecuteSettings: ExecuteSettings(WithTxControl(tx.NewControl(tx.WithTxID(id)))),
+ }
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyTxExecuteOption(settings)
+ }
+ }
+
+ return settings
+}
+
+var _ ExecuteOption = ParametersOption{}
+
+func WithParameters(parameters *params.Parameters) ParametersOption {
+ return ParametersOption(*parameters)
+}
+
+var (
+ _ ExecuteOption = ExecMode(0)
+ _ ExecuteOption = StatsMode(0)
+ _ TxExecuteOption = ExecMode(0)
+ _ TxExecuteOption = StatsMode(0)
+ _ TxExecuteOption = txCommitOption{}
+ _ ExecuteOption = TxControlOption{}
+)
+
+func WithCommit() txCommitOption {
+ return txCommitOption{}
+}
+
+type ExecModeOption = ExecMode
+
+func WithExecMode(mode ExecMode) ExecMode {
+ return mode
+}
+
+type SyntaxOption = Syntax
+
+func WithSyntax(syntax Syntax) SyntaxOption {
+ return syntax
+}
+
+type StatsModeOption = StatsMode
+
+func WithStatsMode(mode StatsMode) StatsMode {
+ return mode
+}
+
+func WithCallOptions(opts ...grpc.CallOption) CallOptions {
+ return opts
+}
+
+func WithTxControl(txControl *tx.Control) TxControlOption {
+ return TxControlOption{txControl}
+}
diff --git a/internal/query/options/retry.go b/internal/query/options/retry.go
new file mode 100644
index 000000000..b604152e3
--- /dev/null
+++ b/internal/query/options/retry.go
@@ -0,0 +1,138 @@
+package options
+
+import (
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/tx"
+ "github.com/ydb-platform/ydb-go-sdk/v3/retry"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+var (
+ _ DoOption = idempotentOption{}
+ _ DoOption = labelOption("")
+ _ DoOption = traceOption{}
+
+ _ DoTxOption = idempotentOption{}
+ _ DoTxOption = labelOption("")
+ _ DoTxOption = traceOption{}
+ _ DoTxOption = doTxSettingsOption{}
+)
+
+type (
+ DoOption interface {
+ applyDoOption(s *doSettings)
+ }
+
+ doSettings struct {
+ retryOpts []retry.Option
+ trace *trace.Query
+ }
+
+ DoTxOption interface {
+ applyDoTxOption(o *doTxSettings)
+ }
+
+ doTxSettings struct {
+ doOpts []DoOption
+ txSettings tx.Settings
+ }
+
+ idempotentOption struct{}
+ labelOption string
+ traceOption struct {
+ t *trace.Query
+ }
+ doTxSettingsOption struct {
+ txSettings tx.Settings
+ }
+)
+
+func (s *doSettings) Trace() *trace.Query {
+ return s.trace
+}
+
+func (s *doSettings) RetryOpts() []retry.Option {
+ return s.retryOpts
+}
+
+func (s *doTxSettings) DoOpts() []DoOption {
+ return s.doOpts
+}
+
+func (s *doTxSettings) TxSettings() tx.Settings {
+ return s.txSettings
+}
+
+func (opt idempotentOption) applyDoTxOption(s *doTxSettings) {
+ s.doOpts = append(s.doOpts, opt)
+}
+
+func (idempotentOption) applyDoOption(s *doSettings) {
+ s.retryOpts = append(s.retryOpts, retry.WithIdempotent(true))
+}
+
+func (opt traceOption) applyDoOption(s *doSettings) {
+ s.trace = s.trace.Compose(opt.t)
+}
+
+func (opt traceOption) applyDoTxOption(s *doTxSettings) {
+ s.doOpts = append(s.doOpts, opt)
+}
+
+func (opt labelOption) applyDoOption(s *doSettings) {
+ s.retryOpts = append(s.retryOpts, retry.WithLabel(string(opt)))
+}
+
+func (opt labelOption) applyDoTxOption(s *doTxSettings) {
+ s.doOpts = append(s.doOpts, opt)
+}
+
+func (opt doTxSettingsOption) applyDoTxOption(opts *doTxSettings) {
+ opts.txSettings = opt.txSettings
+}
+
+func WithTxSettings(txSettings tx.Settings) doTxSettingsOption {
+ return doTxSettingsOption{txSettings: txSettings}
+}
+
+func WithIdempotent() idempotentOption {
+ return idempotentOption{}
+}
+
+func WithLabel(lbl string) labelOption {
+ return labelOption(lbl)
+}
+
+func WithTrace(t *trace.Query) traceOption {
+ return traceOption{t: t}
+}
+
+func ParseDoOpts(t *trace.Query, opts ...DoOption) (s *doSettings) {
+ s = &doSettings{
+ trace: t,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyDoOption(s)
+ }
+ }
+
+ return s
+}
+
+func ParseDoTxOpts(t *trace.Query, opts ...DoTxOption) (s *doTxSettings) {
+ s = &doTxSettings{
+ txSettings: tx.NewSettings(tx.WithDefaultTxMode()),
+ doOpts: []DoOption{
+ WithTrace(t),
+ },
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyDoTxOption(s)
+ }
+ }
+
+ return s
+}
diff --git a/internal/query/result.go b/internal/query/result.go
new file mode 100644
index 000000000..78478610d
--- /dev/null
+++ b/internal/query/result.go
@@ -0,0 +1,204 @@
+package query
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+var _ query.Result = (*result)(nil)
+
+type result struct {
+ stream Ydb_Query_V1.QueryService_ExecuteQueryClient
+ closeOnce func(ctx context.Context) error
+ lastPart *Ydb_Query.ExecuteQueryResponsePart
+ resultSetIndex int64
+ errs []error
+ closed chan struct{}
+ trace *trace.Query
+}
+
+func newResult(
+ ctx context.Context,
+ stream Ydb_Query_V1.QueryService_ExecuteQueryClient,
+ t *trace.Query,
+ closeResult context.CancelFunc,
+) (_ *result, txID string, err error) {
+ if t == nil {
+ t = &trace.Query{}
+ }
+ if closeResult == nil {
+ closeResult = func() {}
+ }
+
+ onDone := trace.QueryOnResultNew(t, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.newResult"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ select {
+ case <-ctx.Done():
+ return nil, txID, xerrors.WithStackTrace(ctx.Err())
+ default:
+ part, err := nextPart(ctx, stream, t)
+ if err != nil {
+ return nil, txID, xerrors.WithStackTrace(err)
+ }
+ var (
+ interrupted = make(chan struct{})
+ closed = make(chan struct{})
+ closeOnce = xsync.OnceFunc(func(ctx context.Context) error {
+ closeResult()
+
+ close(interrupted)
+ close(closed)
+
+ return nil
+ })
+ )
+
+ return &result{
+ stream: stream,
+ resultSetIndex: -1,
+ lastPart: part,
+ closed: closed,
+ closeOnce: closeOnce,
+ trace: t,
+ }, part.GetTxMeta().GetId(), nil
+ }
+}
+
+func nextPart(
+ ctx context.Context,
+ stream Ydb_Query_V1.QueryService_ExecuteQueryClient,
+ t *trace.Query,
+) (_ *Ydb_Query.ExecuteQueryResponsePart, finalErr error) {
+ if t == nil {
+ t = &trace.Query{}
+ }
+
+ onDone := trace.QueryOnResultNextPart(t, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.nextPart"),
+ )
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ part, err := stream.Recv()
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return part, nil
+}
+
+func (r *result) Close(ctx context.Context) (err error) {
+ onDone := trace.QueryOnResultClose(r.trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*result).Close"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ return r.closeOnce(ctx)
+}
+
+func (r *result) nextResultSet(ctx context.Context) (_ *resultSet, err error) {
+ defer func() {
+ if err != nil && !xerrors.Is(err,
+ io.EOF, errClosedResult, context.Canceled,
+ ) {
+ r.errs = append(r.errs, err)
+ }
+ }()
+ nextResultSetIndex := r.resultSetIndex + 1
+ for {
+ select {
+ case <-r.closed:
+ return nil, xerrors.WithStackTrace(errClosedResult)
+ case <-ctx.Done():
+ return nil, xerrors.WithStackTrace(ctx.Err())
+ default:
+ if resultSetIndex := r.lastPart.GetResultSetIndex(); resultSetIndex >= nextResultSetIndex { //nolint:nestif
+ r.resultSetIndex = resultSetIndex
+
+ return newResultSet(func() (_ *Ydb_Query.ExecuteQueryResponsePart, err error) {
+ defer func() {
+ if err != nil && !xerrors.Is(err,
+ io.EOF, context.Canceled,
+ ) {
+ r.errs = append(r.errs, err)
+ }
+ }()
+ select {
+ case <-r.closed:
+ return nil, errClosedResult
+ default:
+ part, err := nextPart(ctx, r.stream, r.trace)
+ if err != nil {
+ if xerrors.Is(err, io.EOF) {
+ _ = r.closeOnce(ctx)
+ }
+
+ return nil, xerrors.WithStackTrace(err)
+ }
+ r.lastPart = part
+ if part.GetResultSetIndex() > nextResultSetIndex {
+ return nil, xerrors.WithStackTrace(fmt.Errorf(
+ "result set (index=%d) receive part (index=%d) for next result set: %w",
+ nextResultSetIndex, part.GetResultSetIndex(), io.EOF,
+ ))
+ }
+
+ return part, nil
+ }
+ }, r.lastPart, r.trace), nil
+ }
+ part, err := nextPart(ctx, r.stream, r.trace)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+ if part.GetResultSetIndex() < r.resultSetIndex {
+ return nil, xerrors.WithStackTrace(fmt.Errorf(
+ "next result set index %d less than last result set index %d: %w",
+ part.GetResultSetIndex(), r.resultSetIndex, errWrongNextResultSetIndex,
+ ))
+ }
+ r.lastPart = part
+ r.resultSetIndex = part.GetResultSetIndex()
+ }
+ }
+}
+
+func (r *result) NextResultSet(ctx context.Context) (_ query.ResultSet, err error) {
+ onDone := trace.QueryOnResultNextResultSet(r.trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*result).NextResultSet"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ return r.nextResultSet(ctx)
+}
+
+func (r *result) Err() error {
+ switch {
+ case len(r.errs) == 0:
+ return nil
+ case len(r.errs) == 1:
+ return r.errs[0]
+ default:
+ return xerrors.WithStackTrace(xerrors.Join(r.errs...))
+ }
+}
diff --git a/internal/query/result_set.go b/internal/query/result_set.go
new file mode 100644
index 000000000..47d90301f
--- /dev/null
+++ b/internal/query/result_set.go
@@ -0,0 +1,96 @@
+package query
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+var _ query.ResultSet = (*resultSet)(nil)
+
+type resultSet struct {
+ index int64
+ recv func() (*Ydb_Query.ExecuteQueryResponsePart, error)
+ columns []*Ydb.Column
+ currentPart *Ydb_Query.ExecuteQueryResponsePart
+ rowIndex int
+ trace *trace.Query
+ done chan struct{}
+}
+
+func newResultSet(
+ recv func() (*Ydb_Query.ExecuteQueryResponsePart, error),
+ part *Ydb_Query.ExecuteQueryResponsePart,
+ t *trace.Query,
+) *resultSet {
+ if t == nil {
+ t = &trace.Query{}
+ }
+
+ return &resultSet{
+ index: part.GetResultSetIndex(),
+ recv: recv,
+ currentPart: part,
+ rowIndex: -1,
+ columns: part.GetResultSet().GetColumns(),
+ trace: t,
+ done: make(chan struct{}),
+ }
+}
+
+func (rs *resultSet) nextRow(ctx context.Context) (*row, error) {
+ rs.rowIndex++
+ select {
+ case <-rs.done:
+ return nil, io.EOF
+ case <-ctx.Done():
+ return nil, xerrors.WithStackTrace(ctx.Err())
+ default:
+ if rs.rowIndex == len(rs.currentPart.GetResultSet().GetRows()) {
+ part, err := rs.recv()
+ if err != nil {
+ if xerrors.Is(err, io.EOF) {
+ close(rs.done)
+ }
+
+ return nil, xerrors.WithStackTrace(err)
+ }
+ rs.rowIndex = 0
+ rs.currentPart = part
+ if part == nil {
+ close(rs.done)
+
+ return nil, xerrors.WithStackTrace(io.EOF)
+ }
+ }
+ if rs.index != rs.currentPart.GetResultSetIndex() {
+ close(rs.done)
+
+ return nil, xerrors.WithStackTrace(fmt.Errorf(
+ "received part with result set index = %d, current result set index = %d: %w",
+ rs.index, rs.currentPart.GetResultSetIndex(), errWrongResultSetIndex,
+ ))
+ }
+
+ return newRow(ctx, rs.columns, rs.currentPart.GetResultSet().GetRows()[rs.rowIndex], rs.trace)
+ }
+}
+
+func (rs *resultSet) NextRow(ctx context.Context) (_ query.Row, err error) {
+ onDone := trace.QueryOnResultSetNextRow(rs.trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*resultSet).NextRow"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ return rs.nextRow(ctx)
+}
diff --git a/internal/query/result_set_test.go b/internal/query/result_set_test.go
new file mode 100644
index 000000000..a011d2781
--- /dev/null
+++ b/internal/query/result_set_test.go
@@ -0,0 +1,602 @@
+package query
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "go.uber.org/mock/gomock"
+ grpcCodes "google.golang.org/grpc/codes"
+ grpcStatus "google.golang.org/grpc/status"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestResultSetNext(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ t.Run("OverTwoParts", func(t *testing.T) {
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, io.EOF)
+ recv, err := stream.Recv()
+ require.NoError(t, err)
+ rs := newResultSet(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
+ part, err := stream.Recv()
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return part, nil
+ }, recv, nil)
+ require.EqualValues(t, 0, rs.index)
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ })
+ t.Run("CanceledContext", func(t *testing.T) {
+ ctx, cancel := context.WithCancel(xtest.Context(t))
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ recv, err := stream.Recv()
+ require.NoError(t, err)
+ rs := newResultSet(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
+ part, err := stream.Recv()
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return part, nil
+ }, recv, nil)
+ require.EqualValues(t, 0, rs.index)
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ cancel()
+ {
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, context.Canceled)
+ }
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, xerrors.Operation(xerrors.WithStatusCode(
+ Ydb.StatusIds_OVERLOADED,
+ )))
+ recv, err := stream.Recv()
+ require.NoError(t, err)
+ rs := newResultSet(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
+ part, err := nextPart(ctx, stream, nil)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+ if resultSetIndex := part.GetResultSetIndex(); resultSetIndex != 0 {
+ return nil, xerrors.WithStackTrace(fmt.Errorf(
+ "critical violation of the logic: wrong result set index: %d != %d",
+ resultSetIndex, 0,
+ ))
+ }
+
+ return part, nil
+ }, recv, nil)
+ require.EqualValues(t, 0, rs.index)
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_OVERLOADED))
+ }
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ recv, err := stream.Recv()
+ require.NoError(t, err)
+ rs := newResultSet(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
+ part, err := nextPart(ctx, stream, nil)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+ if resultSetIndex := part.GetResultSetIndex(); resultSetIndex != 0 {
+ return nil, xerrors.WithStackTrace(fmt.Errorf(
+ "critical violation of the logic: wrong result set index: %d != %d",
+ resultSetIndex, 0,
+ ))
+ }
+
+ return part, nil
+ }, recv, nil)
+ require.EqualValues(t, 0, rs.index)
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ }
+ })
+ t.Run("WrongResultSetIndex", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 1,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ recv, err := stream.Recv()
+ require.NoError(t, err)
+ rs := newResultSet(func() (*Ydb_Query.ExecuteQueryResponsePart, error) {
+ part, err := nextPart(ctx, stream, nil)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return part, nil
+ }, recv, nil)
+ require.EqualValues(t, 0, rs.index)
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, errWrongResultSetIndex)
+ }
+ {
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ })
+}
diff --git a/internal/query/result_test.go b/internal/query/result_test.go
new file mode 100644
index 000000000..10d408f03
--- /dev/null
+++ b/internal/query/result_test.go
@@ -0,0 +1,895 @@
+package query
+
+import (
+ "context"
+ "io"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "go.uber.org/mock/gomock"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestResultNextResultSet(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx, cancel := context.WithCancel(xtest.Context(t))
+ defer cancel()
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 1,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "d",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "e",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 1,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 2,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "d",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "e",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 2,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(nil, io.EOF)
+ r, _, err := newResult(ctx, stream, nil, nil)
+ require.NoError(t, err)
+ defer r.Close(ctx)
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=5)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=6)")
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ }
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.index)
+ }
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=5)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=6)")
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ }
+ {
+ t.Log("close result")
+ r.Close(context.Background())
+ }
+ {
+ t.Log("nextResultSet")
+ _, err := r.nextResultSet(context.Background())
+ require.ErrorIs(t, err, errClosedResult)
+ }
+ t.Log("check final error")
+ require.NoError(t, r.Err())
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("InterruptStream", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx, cancel := context.WithCancel(xtest.Context(t))
+ defer cancel()
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ r, _, err := newResult(ctx, stream, nil, nil)
+ require.NoError(t, err)
+ defer r.Close(ctx)
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ t.Log("explicit interrupt stream")
+ require.NoError(t, r.closeOnce(ctx))
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(context.Background())
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(context.Background())
+ require.ErrorIs(t, err, errClosedResult)
+ }
+ }
+ {
+ t.Log("nextResultSet")
+ _, err := r.nextResultSet(context.Background())
+ require.ErrorIs(t, err, errClosedResult)
+ }
+ t.Log("check final error")
+ require.ErrorIs(t, r.Err(), errClosedResult)
+ }, xtest.StopAfter(time.Second))
+ })
+ t.Run("WrongResultSetIndex", func(t *testing.T) {
+ xtest.TestManyTimes(t, func(t testing.TB) {
+ ctx, cancel := context.WithCancel(xtest.Context(t))
+ defer cancel()
+ ctrl := gomock.NewController(t)
+ stream := NewMockQueryService_ExecuteQueryClient(ctrl)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 0,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 2,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "d",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "e",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 2,
+ ResultSet: &Ydb.ResultSet{
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 3,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "3",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 4,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "4",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 5,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "5",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ stream.EXPECT().Recv().Return(&Ydb_Query.ExecuteQueryResponsePart{
+ Status: Ydb.StatusIds_SUCCESS,
+ ResultSetIndex: 1,
+ ResultSet: &Ydb.ResultSet{
+ Columns: []*Ydb.Column{
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ {
+ Name: "d",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "e",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ Rows: []*Ydb.Value{
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 1,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "1",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ }},
+ },
+ {
+ Items: []*Ydb.Value{{
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 2,
+ },
+ }, {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "2",
+ },
+ }, {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: false,
+ },
+ }},
+ },
+ },
+ },
+ }, nil)
+ r, _, err := newResult(ctx, stream, nil, nil)
+ require.NoError(t, err)
+ defer r.Close(ctx)
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.index)
+ {
+ t.Log("next (row=1)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=2)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=3)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=4)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 0, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=5)")
+ _, err := rs.nextRow(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 1, rs.rowIndex)
+ }
+ {
+ t.Log("next (row=6)")
+ _, err := rs.nextRow(ctx)
+ require.ErrorIs(t, err, io.EOF)
+ }
+ }
+ {
+ t.Log("nextResultSet")
+ rs, err := r.nextResultSet(ctx)
+ require.NoError(t, err)
+ require.EqualValues(t, 2, rs.index)
+ }
+ {
+ t.Log("nextResultSet")
+ _, err := r.nextResultSet(ctx)
+ require.ErrorIs(t, err, errWrongNextResultSetIndex)
+ }
+ t.Log("check final error")
+ require.ErrorIs(t, r.Err(), errWrongNextResultSetIndex)
+ }, xtest.StopAfter(time.Second))
+ })
+}
diff --git a/internal/query/row.go b/internal/query/row.go
new file mode 100644
index 000000000..476b6aa14
--- /dev/null
+++ b/internal/query/row.go
@@ -0,0 +1,68 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/scanner"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+var _ query.Row = (*row)(nil)
+
+type row struct {
+ ctx context.Context
+ trace *trace.Query
+
+ indexedScanner scanner.IndexedScanner
+ namedScanner scanner.NamedScanner
+ structScanner scanner.StructScanner
+}
+
+func newRow(ctx context.Context, columns []*Ydb.Column, v *Ydb.Value, t *trace.Query) (*row, error) {
+ data := scanner.Data(columns, v.GetItems())
+
+ return &row{
+ ctx: ctx,
+ trace: t,
+ indexedScanner: scanner.Indexed(data),
+ namedScanner: scanner.Named(data),
+ structScanner: scanner.Struct(data),
+ }, nil
+}
+
+func (r row) Scan(dst ...interface{}) (err error) {
+ onDone := trace.QueryOnRowScan(r.trace, &r.ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.row.Scan"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ return r.indexedScanner.Scan(dst...)
+}
+
+func (r row) ScanNamed(dst ...scanner.NamedDestination) (err error) {
+ onDone := trace.QueryOnRowScanNamed(r.trace, &r.ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.row.ScanNamed"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ return r.namedScanner.ScanNamed(dst...)
+}
+
+func (r row) ScanStruct(dst interface{}, opts ...scanner.ScanStructOption) (err error) {
+ onDone := trace.QueryOnRowScanStruct(r.trace, &r.ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.row.ScanStruct"),
+ )
+ defer func() {
+ onDone(err)
+ }()
+
+ return r.structScanner.ScanStruct(dst, opts...)
+}
diff --git a/internal/query/scanner/data.go b/internal/query/scanner/data.go
new file mode 100644
index 000000000..d1a806097
--- /dev/null
+++ b/internal/query/scanner/data.go
@@ -0,0 +1,36 @@
+package scanner
+
+import (
+ "fmt"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+)
+
+type data struct {
+ columns []*Ydb.Column
+ values []*Ydb.Value
+}
+
+func Data(columns []*Ydb.Column, values []*Ydb.Value) *data {
+ return &data{
+ columns: columns,
+ values: values,
+ }
+}
+
+func (s data) seekByName(name string) (value.Value, error) {
+ for i := range s.columns {
+ if s.columns[i].GetName() == name {
+ return value.FromYDB(s.columns[i].GetType(), s.values[i]), nil
+ }
+ }
+
+ return nil, xerrors.WithStackTrace(fmt.Errorf("'%s': %w", name, errColumnsNotFoundInRow))
+}
+
+func (s data) seekByIndex(idx int) value.Value {
+ return value.FromYDB(s.columns[idx].GetType(), s.values[idx])
+}
diff --git a/internal/query/scanner/errors.go b/internal/query/scanner/errors.go
new file mode 100644
index 000000000..cac104f10
--- /dev/null
+++ b/internal/query/scanner/errors.go
@@ -0,0 +1,13 @@
+package scanner
+
+import (
+ "errors"
+)
+
+var (
+ errColumnsNotFoundInRow = errors.New("some columns not found in row")
+ errFieldsNotFoundInStruct = errors.New("some fields not found in struct")
+ errIncompatibleColumnsAndDestinations = errors.New("incompatible columns and destinations")
+ errDstTypeIsNotAPointer = errors.New("dst type is not a pointer")
+ errDstTypeIsNotAPointerToStruct = errors.New("dst type is not a pointer to struct")
+)
diff --git a/internal/query/scanner/indexed.go b/internal/query/scanner/indexed.go
new file mode 100644
index 000000000..e826a08c0
--- /dev/null
+++ b/internal/query/scanner/indexed.go
@@ -0,0 +1,37 @@
+package scanner
+
+import (
+ "fmt"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+)
+
+type IndexedScanner struct {
+ data *data
+}
+
+func Indexed(data *data) IndexedScanner {
+ return IndexedScanner{
+ data: data,
+ }
+}
+
+func (s IndexedScanner) Scan(dst ...interface{}) (err error) {
+ if len(dst) != len(s.data.columns) {
+ return xerrors.WithStackTrace(
+ fmt.Errorf("%w: %d != %d",
+ errIncompatibleColumnsAndDestinations,
+ len(dst), len(s.data.columns),
+ ),
+ )
+ }
+ for i := range dst {
+ v := s.data.seekByIndex(i)
+ if err := value.CastTo(v, dst[i]); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+ }
+
+ return nil
+}
diff --git a/internal/query/scanner/indexed_test.go b/internal/query/scanner/indexed_test.go
new file mode 100644
index 000000000..ac2a9b40d
--- /dev/null
+++ b/internal/query/scanner/indexed_test.go
@@ -0,0 +1,558 @@
+package scanner
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+func TestIndexed(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ s IndexedScanner
+ dst [][]interface{}
+ exp [][]interface{}
+ }{
+ {
+ name: "Ydb.Type_UTF8",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v string) *string { return &v }("")},
+ {func(v []byte) *[]byte { return &v }([]byte(""))},
+ },
+ exp: [][]interface{}{
+ {func(v string) *string { return &v }("test")},
+ {func(v []byte) *[]byte { return &v }([]byte("test"))},
+ },
+ },
+ {
+ name: "Ydb.Type_STRING",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_STRING,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v string) *string { return &v }("")},
+ {func(v []byte) *[]byte { return &v }([]byte(""))},
+ },
+ exp: [][]interface{}{
+ {func(v string) *string { return &v }("test")},
+ {func(v []byte) *[]byte { return &v }([]byte("test"))},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT64",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT64",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT32",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v uint32) *uint32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT32",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT32,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v int) *int { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v int) *int { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT16",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v uint32) *uint32 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT16",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT8",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v uint8) *uint8 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v uint32) *uint32 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v uint8) *uint8 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT8",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v int8) *int8 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v int8) *int8 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_BOOL",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v bool) *bool { return &v }(false)},
+ },
+ exp: [][]interface{}{
+ {func(v bool) *bool { return &v }(true)},
+ },
+ },
+ {
+ name: "Ydb.Type_DATE",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATE,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 100500,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(100500)},
+ {func(v int64) *int64 { return &v }(100500)},
+ {func(v int32) *int32 { return &v }(100500)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(8683200000, 0))},
+ },
+ },
+ {
+ name: "Ydb.Type_DATETIME",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATETIME,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 100500,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(100500)},
+ {func(v int64) *int64 { return &v }(100500)},
+ {func(v uint32) *uint32 { return &v }(100500)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(100500, 0))},
+ },
+ },
+ {
+ name: "Ydb.Type_TIMESTAMP",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TIMESTAMP,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 12345678987654321,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(12345678987654321)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(12345678987, 654321000))},
+ },
+ },
+ {
+ name: "Ydb.Type_INTERVAL",
+ s: Indexed(Data(
+ []*Ydb.Column{
+ {
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INTERVAL,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 100500,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v time.Duration) *time.Duration { return &v }(time.Duration(0))},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(100500)},
+ {func(v time.Duration) *time.Duration { return &v }(time.Duration(100500000))},
+ },
+ },
+ } {
+ for i := range tt.dst {
+ t.Run(tt.name+"→"+reflect.TypeOf(tt.dst[i][0]).Elem().String(), func(t *testing.T) {
+ err := tt.s.Scan(tt.dst[i]...)
+ require.NoError(t, err)
+ require.Equal(t, tt.exp[i], tt.dst[i])
+ })
+ }
+ }
+}
+
+func TestIndexedIncompatibleColumnsAndDestinations(t *testing.T) {
+ scanner := &IndexedScanner{data: Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ )}
+ var (
+ B string
+ C string
+ )
+ err := scanner.Scan(&B, &C)
+ require.ErrorIs(t, err, errIncompatibleColumnsAndDestinations)
+}
+
+func TestIndexedCastFailed(t *testing.T) {
+ scanner := Indexed(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var A uint64
+ err := scanner.Scan(&A)
+ require.ErrorIs(t, err, value.ErrCannotCast)
+}
diff --git a/internal/query/scanner/named.go b/internal/query/scanner/named.go
new file mode 100644
index 000000000..07f31aa94
--- /dev/null
+++ b/internal/query/scanner/named.go
@@ -0,0 +1,53 @@
+package scanner
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+)
+
+type (
+ NamedScanner struct {
+ data *data
+ }
+ NamedDestination struct {
+ name string
+ ref interface{}
+ }
+)
+
+func NamedRef(columnName string, destinationValueReference interface{}) (dst NamedDestination) {
+ if columnName == "" {
+ panic("columnName must be not empty")
+ }
+ dst.name = columnName
+ v := reflect.TypeOf(destinationValueReference)
+ if v.Kind() != reflect.Ptr {
+ panic(fmt.Errorf("%T is not reference type", destinationValueReference))
+ }
+ dst.ref = destinationValueReference
+
+ return dst
+}
+
+func Named(data *data) NamedScanner {
+ return NamedScanner{
+ data: data,
+ }
+}
+
+func (s NamedScanner) ScanNamed(dst ...NamedDestination) (err error) {
+ for i := range dst {
+ v, err := s.data.seekByName(dst[i].name)
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+ if err = value.CastTo(v, dst[i].ref); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+ }
+
+ return nil
+}
diff --git a/internal/query/scanner/named_test.go b/internal/query/scanner/named_test.go
new file mode 100644
index 000000000..7c3fe9d8b
--- /dev/null
+++ b/internal/query/scanner/named_test.go
@@ -0,0 +1,701 @@
+package scanner
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+func TestNamed(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ s NamedScanner
+ dst [][]interface{}
+ exp [][]interface{}
+ }{
+ {
+ name: "Ydb.Type_UTF8",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v string) *string { return &v }("")},
+ {func(v []byte) *[]byte { return &v }([]byte(""))},
+ },
+ exp: [][]interface{}{
+ {func(v string) *string { return &v }("test")},
+ {func(v []byte) *[]byte { return &v }([]byte("test"))},
+ },
+ },
+ {
+ name: "Ydb.Type_STRING",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_STRING,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("test"),
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v string) *string { return &v }("")},
+ {func(v []byte) *[]byte { return &v }([]byte(""))},
+ },
+ exp: [][]interface{}{
+ {func(v string) *string { return &v }("test")},
+ {func(v []byte) *[]byte { return &v }([]byte("test"))},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT64",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT64",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT32",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v uint32) *uint32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT32",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT32,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v int) *int { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v int) *int { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT16",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v uint32) *uint32 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT16",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_UINT8",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v uint8) *uint8 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(123)},
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v uint32) *uint32 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v uint8) *uint8 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_INT8",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v int8) *int8 { return &v }(0)},
+ {func(v float32) *float32 { return &v }(0)},
+ {func(v float64) *float64 { return &v }(0)},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(123)},
+ {func(v int32) *int32 { return &v }(123)},
+ {func(v int8) *int8 { return &v }(123)},
+ {func(v float32) *float32 { return &v }(123)},
+ {func(v float64) *float64 { return &v }(123)},
+ },
+ },
+ {
+ name: "Ydb.Type_BOOL",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v bool) *bool { return &v }(false)},
+ },
+ exp: [][]interface{}{
+ {func(v bool) *bool { return &v }(true)},
+ },
+ },
+ {
+ name: "Ydb.Type_DATE",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATE,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 100500,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v int32) *int32 { return &v }(0)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(100500)},
+ {func(v int64) *int64 { return &v }(100500)},
+ {func(v int32) *int32 { return &v }(100500)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(8683200000, 0))},
+ },
+ },
+ {
+ name: "Ydb.Type_DATETIME",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATETIME,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 100500,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v uint32) *uint32 { return &v }(0)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(100500)},
+ {func(v int64) *int64 { return &v }(100500)},
+ {func(v uint32) *uint32 { return &v }(100500)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(100500, 0))},
+ },
+ },
+ {
+ name: "Ydb.Type_TIMESTAMP",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TIMESTAMP,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 12345678987654321,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(0)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
+ },
+ exp: [][]interface{}{
+ {func(v uint64) *uint64 { return &v }(12345678987654321)},
+ {func(v time.Time) *time.Time { return &v }(time.Unix(12345678987, 654321000))},
+ },
+ },
+ {
+ name: "Ydb.Type_INTERVAL",
+ s: Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INTERVAL,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 100500,
+ },
+ },
+ },
+ )),
+ dst: [][]interface{}{
+ {func(v int64) *int64 { return &v }(0)},
+ {func(v time.Duration) *time.Duration { return &v }(time.Duration(0))},
+ },
+ exp: [][]interface{}{
+ {func(v int64) *int64 { return &v }(100500)},
+ {func(v time.Duration) *time.Duration { return &v }(time.Duration(100500000))},
+ },
+ },
+ } {
+ for i := range tt.dst {
+ t.Run(tt.name+"→"+reflect.TypeOf(tt.dst[i][0]).Elem().String(), func(t *testing.T) {
+ err := tt.s.ScanNamed(func() (dst []NamedDestination) {
+ for j := range tt.dst[i] {
+ dst = append(dst, NamedRef("a", tt.dst[i][j]))
+ }
+
+ return dst
+ }()...)
+ require.NoError(t, err)
+ require.Equal(t, tt.exp[i], tt.dst[i])
+ })
+ }
+ }
+}
+
+func TestScannerNamedNotFoundByName(t *testing.T) {
+ scanner := Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var s string
+ err := scanner.ScanNamed(NamedRef("b", &s))
+ require.ErrorIs(t, err, errColumnsNotFoundInRow)
+}
+
+func TestScannerNamedOrdering(t *testing.T) {
+ scanner := Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "b",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "c",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "A",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "B",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "C",
+ },
+ },
+ },
+ ))
+ var a, b, c string
+ err := scanner.ScanNamed(
+ NamedRef("c", &c),
+ NamedRef("b", &b),
+ NamedRef("a", &a),
+ )
+ require.NoError(t, err)
+ require.Equal(t, "A", a)
+ require.Equal(t, "B", b)
+ require.Equal(t, "C", c)
+}
+
+func TestNamedRef(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ ref interface{}
+ dst NamedDestination
+ panic bool
+ }{
+ {
+ name: "",
+ ref: nil,
+ dst: NamedDestination{},
+ panic: true,
+ },
+ {
+ name: "nil_ref",
+ ref: nil,
+ dst: NamedDestination{},
+ panic: true,
+ },
+ {
+ name: "not_ref",
+ ref: 123,
+ dst: NamedDestination{},
+ panic: true,
+ },
+ {
+ name: "int_ptr",
+ ref: func(v int) *int { return &v }(123),
+ dst: NamedDestination{
+ name: "int_ptr",
+ ref: func(v int) *int { return &v }(123),
+ },
+ panic: false,
+ },
+ {
+ name: "int_dbl_ptr",
+ ref: func(v int) **int {
+ vv := &v
+
+ return &vv
+ }(123),
+ dst: NamedDestination{
+ name: "int_dbl_ptr",
+ ref: func(v int) **int {
+ vv := &v
+
+ return &vv
+ }(123),
+ },
+ panic: false,
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ if tt.panic {
+ defer func() {
+ require.NotNil(t, recover())
+ }()
+ } else {
+ defer func() {
+ require.Nil(t, recover())
+ }()
+ }
+ require.Equal(t, tt.dst, NamedRef(tt.name, tt.ref))
+ })
+ }
+}
+
+func TestNamedCastFailed(t *testing.T) {
+ scanner := Named(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var A uint64
+ err := scanner.ScanNamed(NamedRef("a", &A))
+ require.ErrorIs(t, err, value.ErrCannotCast)
+}
diff --git a/internal/query/scanner/struct.go b/internal/query/scanner/struct.go
new file mode 100644
index 000000000..5f73ff095
--- /dev/null
+++ b/internal/query/scanner/struct.go
@@ -0,0 +1,91 @@
+package scanner
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+)
+
+type scanStructSettings struct {
+ TagName string
+ AllowMissingColumnsFromSelect bool
+ AllowMissingFieldsInStruct bool
+}
+
+type StructScanner struct {
+ data *data
+}
+
+func Struct(data *data) StructScanner {
+ return StructScanner{
+ data: data,
+ }
+}
+
+func fieldName(f reflect.StructField, tagName string) string { //nolint:gocritic
+ if name, has := f.Tag.Lookup(tagName); has {
+ return name
+ }
+
+ return f.Name
+}
+
+func (s StructScanner) ScanStruct(dst interface{}, opts ...ScanStructOption) (err error) {
+ settings := scanStructSettings{
+ TagName: "sql",
+ AllowMissingColumnsFromSelect: false,
+ AllowMissingFieldsInStruct: false,
+ }
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyScanStructOption(&settings)
+ }
+ }
+ ptr := reflect.ValueOf(dst)
+ if ptr.Kind() != reflect.Pointer {
+ return xerrors.WithStackTrace(fmt.Errorf("%w: '%s'", errDstTypeIsNotAPointer, ptr.Kind().String()))
+ }
+ if ptr.Elem().Kind() != reflect.Struct {
+ return xerrors.WithStackTrace(fmt.Errorf("%w: '%s'", errDstTypeIsNotAPointerToStruct, ptr.Elem().Kind().String()))
+ }
+ tt := ptr.Elem().Type()
+ missingColumns := make([]string, 0, len(s.data.columns))
+ existingFields := make(map[string]struct{}, tt.NumField())
+ for i := 0; i < tt.NumField(); i++ {
+ name := fieldName(tt.Field(i), settings.TagName)
+ v, err := s.data.seekByName(name)
+ if err != nil {
+ missingColumns = append(missingColumns, name)
+ } else {
+ if err = value.CastTo(v, ptr.Elem().Field(i).Addr().Interface()); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+ existingFields[name] = struct{}{}
+ }
+ }
+
+ if !settings.AllowMissingColumnsFromSelect && len(missingColumns) > 0 {
+ return xerrors.WithStackTrace(
+ fmt.Errorf("%w: '%v'", errColumnsNotFoundInRow, strings.Join(missingColumns, "','")),
+ )
+ }
+
+ if !settings.AllowMissingFieldsInStruct {
+ missingFields := make([]string, 0, tt.NumField())
+ for _, c := range s.data.columns {
+ if _, has := existingFields[c.GetName()]; !has {
+ missingFields = append(missingFields, c.GetName())
+ }
+ }
+ if len(missingFields) > 0 {
+ return xerrors.WithStackTrace(
+ fmt.Errorf("%w: '%v'", errFieldsNotFoundInStruct, strings.Join(missingFields, "','")),
+ )
+ }
+ }
+
+ return nil
+}
diff --git a/internal/query/scanner/struct_options.go b/internal/query/scanner/struct_options.go
new file mode 100644
index 000000000..3146f6750
--- /dev/null
+++ b/internal/query/scanner/struct_options.go
@@ -0,0 +1,40 @@
+package scanner
+
+type (
+ ScanStructOption interface {
+ applyScanStructOption(settings *scanStructSettings)
+ }
+ tagName string
+ allowMissingColumnsFromSelect struct{}
+ allowMissingFieldsInStruct struct{}
+)
+
+var (
+ _ ScanStructOption = tagName("")
+ _ ScanStructOption = allowMissingColumnsFromSelect{}
+ _ ScanStructOption = allowMissingFieldsInStruct{}
+)
+
+func (allowMissingFieldsInStruct) applyScanStructOption(settings *scanStructSettings) {
+ settings.AllowMissingFieldsInStruct = true
+}
+
+func (allowMissingColumnsFromSelect) applyScanStructOption(settings *scanStructSettings) {
+ settings.AllowMissingColumnsFromSelect = true
+}
+
+func (name tagName) applyScanStructOption(settings *scanStructSettings) {
+ settings.TagName = string(name)
+}
+
+func WithTagName(name string) tagName {
+ return tagName(name)
+}
+
+func WithAllowMissingColumnsFromSelect() allowMissingColumnsFromSelect {
+ return allowMissingColumnsFromSelect{}
+}
+
+func WithAllowMissingFieldsInStruct() allowMissingFieldsInStruct {
+ return allowMissingFieldsInStruct{}
+}
diff --git a/internal/query/scanner/struct_test.go b/internal/query/scanner/struct_test.go
new file mode 100644
index 000000000..d6d1918ea
--- /dev/null
+++ b/internal/query/scanner/struct_test.go
@@ -0,0 +1,853 @@
+package scanner
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestFieldName(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ in interface{}
+ out string
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ in: struct {
+ Col0 string
+ }{},
+ out: "Col0",
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ in: struct {
+ Col0 string `sql:"col0"`
+ }{},
+ out: "col0",
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ require.Equal(t, tt.out, fieldName(reflect.ValueOf(tt.in).Type().Field(0), "sql"))
+ })
+ }
+}
+
+func TestStruct(t *testing.T) {
+ newScannerData := func(mapping map[*Ydb.Column]*Ydb.Value) *data {
+ data := &data{
+ columns: make([]*Ydb.Column, 0, len(mapping)),
+ values: make([]*Ydb.Value, 0, len(mapping)),
+ }
+ for c, v := range mapping {
+ data.columns = append(data.columns, c)
+ data.values = append(data.values, v)
+ }
+
+ return data
+ }
+
+ type scanData struct { //nolint:maligned
+ Utf8String string
+ Utf8Bytes []byte
+ StringString string
+ StringBytes []byte
+ Uint64Uint64 uint64
+ Int64Int64 int64
+ Uint32Uint64 uint64
+ Uint32Int64 int64
+ Uint32Uint32 uint32
+ Int32Int64 int64
+ Int32Int32 int32
+ Uint16Uint64 uint64
+ Uint16Int64 int64
+ Uint16Uint32 uint32
+ Uint16Int32 int32
+ Uint16Uint16 uint16
+ Int16Int64 int64
+ Int16Int32 int32
+ Uint8Uint64 uint64
+ Uint8Int64 int64
+ Uint8Uint32 uint32
+ Uint8Int32 int32
+ Uint8Uint16 uint16
+ Int8Int64 int64
+ Int8Int32 int32
+ Int8Int16 int16
+ BoolBool bool
+ DateTime time.Time
+ DatetimeTime time.Time
+ TimestampTime time.Time
+ }
+ var dst scanData
+ err := Struct(newScannerData(map[*Ydb.Column]*Ydb.Value{
+ {
+ Name: "Utf8String",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "A",
+ },
+ },
+ {
+ Name: "Utf8Bytes",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "A",
+ },
+ },
+ {
+ Name: "StringString",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_STRING,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("A"),
+ },
+ },
+ {
+ Name: "StringBytes",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_STRING,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_BytesValue{
+ BytesValue: []byte("A"),
+ },
+ },
+ {
+ Name: "Uint64Uint64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT64,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 123,
+ },
+ },
+ {
+ Name: "Int64Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT64,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int64Value{
+ Int64Value: 123,
+ },
+ },
+ {
+ Name: "Uint32Uint64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint32Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint32Uint32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT32,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Int32Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT32,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "Int32Int32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT32,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "Uint16Uint64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint16Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint16Uint32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint16Int32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint16Uint16",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Int16Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "Int16Int32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "Uint8Uint64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint8Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint8Uint32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint8Int32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Uint8Uint16",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UINT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 123,
+ },
+ },
+ {
+ Name: "Int8Int64",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "Int8Int32",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "Int8Int16",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_INT16,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Int32Value{
+ Int32Value: 123,
+ },
+ },
+ {
+ Name: "BoolBool",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_BOOL,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ {
+ Name: "DateTime",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATE,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 100500,
+ },
+ },
+ {
+ Name: "DatetimeTime",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_DATETIME,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint32Value{
+ Uint32Value: 100500,
+ },
+ },
+ {
+ Name: "TimestampTime",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_TIMESTAMP,
+ },
+ },
+ }: {
+ Value: &Ydb.Value_Uint64Value{
+ Uint64Value: 12345678987654321,
+ },
+ },
+ })).ScanStruct(&dst)
+ require.NoError(t, err)
+ require.Equal(t, scanData{
+ Utf8String: "A",
+ Utf8Bytes: []byte("A"),
+ StringString: "A",
+ StringBytes: []byte("A"),
+ Uint64Uint64: 123,
+ Int64Int64: 123,
+ Uint32Uint64: 123,
+ Uint32Int64: 123,
+ Uint32Uint32: 123,
+ Int32Int64: 123,
+ Int32Int32: 123,
+ Uint16Uint64: 123,
+ Uint16Int64: 123,
+ Uint16Uint32: 123,
+ Uint16Int32: 123,
+ Uint16Uint16: 123,
+ Int16Int64: 123,
+ Int16Int32: 123,
+ Uint8Uint64: 123,
+ Uint8Int64: 123,
+ Uint8Uint32: 123,
+ Uint8Int32: 123,
+ Uint8Uint16: 123,
+ Int8Int64: 123,
+ Int8Int32: 123,
+ Int8Int16: 123,
+ BoolBool: true,
+ DateTime: time.Unix(8683200000, 0),
+ DatetimeTime: time.Unix(100500, 0),
+ TimestampTime: time.Unix(12345678987, 654321000),
+ }, dst)
+}
+
+func TestStructNotAPointer(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row struct {
+ B string
+ C string
+ }
+ err := scanner.ScanStruct(row)
+ require.ErrorIs(t, err, errDstTypeIsNotAPointer)
+}
+
+func TestStructNotAPointerToStruct(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row string
+ err := scanner.ScanStruct(&row)
+ require.ErrorIs(t, err, errDstTypeIsNotAPointerToStruct)
+}
+
+func TestStructCastFailed(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "A",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row struct {
+ A uint64
+ }
+ err := scanner.ScanStruct(&row)
+ require.ErrorIs(t, err, value.ErrCannotCast)
+}
+
+func TestStructNotFoundColumns(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "a",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row struct {
+ B string
+ C string
+ }
+ err := scanner.ScanStruct(&row)
+ require.ErrorIs(t, err, errColumnsNotFoundInRow)
+}
+
+func TestStructWithAllowMissingColumnsFromSelect(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "A",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row struct {
+ A string
+ B string
+ C string
+ }
+ err := scanner.ScanStruct(&row,
+ WithAllowMissingColumnsFromSelect(),
+ )
+ require.NoError(t, err)
+ require.Equal(t, "test", row.A)
+ require.Equal(t, "", row.B)
+ require.Equal(t, "", row.C)
+}
+
+func TestStructNotFoundFields(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "A",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "B",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "C",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row struct {
+ A string
+ }
+ err := scanner.ScanStruct(&row)
+ require.ErrorIs(t, err, errFieldsNotFoundInStruct)
+}
+
+func TestStructWithAllowMissingFieldsInStruct(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "A",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "B",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "C",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "test",
+ },
+ },
+ },
+ ))
+ var row struct {
+ A string
+ }
+ err := scanner.ScanStruct(&row,
+ WithAllowMissingFieldsInStruct(),
+ )
+ require.NoError(t, err)
+ require.Equal(t, "test", row.A)
+}
+
+func TestStructWithTagName(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "A",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "B",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "C",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "AA",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "BB",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "CC",
+ },
+ },
+ },
+ ))
+ var row struct {
+ A string `test:"A"`
+ B string `test:"B"`
+ C string `test:"C"`
+ }
+ err := scanner.ScanStruct(&row,
+ WithTagName("test"),
+ )
+ require.NoError(t, err)
+ require.Equal(t, "AA", row.A)
+ require.Equal(t, "BB", row.B)
+ require.Equal(t, "CC", row.C)
+}
+
+func TestScannerStructOrdering(t *testing.T) {
+ scanner := Struct(Data(
+ []*Ydb.Column{
+ {
+ Name: "B",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "A",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ {
+ Name: "C",
+ Type: &Ydb.Type{
+ Type: &Ydb.Type_TypeId{
+ TypeId: Ydb.Type_UTF8,
+ },
+ },
+ },
+ },
+ []*Ydb.Value{
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "B",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "A",
+ },
+ },
+ {
+ Value: &Ydb.Value_TextValue{
+ TextValue: "C",
+ },
+ },
+ },
+ ))
+ var row struct {
+ A string
+ B string
+ C string
+ }
+ err := scanner.ScanStruct(&row)
+ require.NoError(t, err)
+ require.Equal(t, "A", row.A)
+ require.Equal(t, "B", row.B)
+ require.Equal(t, "C", row.C)
+}
diff --git a/internal/query/session.go b/internal/query/session.go
new file mode 100644
index 000000000..708b36df6
--- /dev/null
+++ b/internal/query/session.go
@@ -0,0 +1,287 @@
+package query
+
+import (
+ "context"
+ "io"
+ "sync/atomic"
+
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+var _ query.Session = (*Session)(nil)
+
+type (
+ Session struct {
+ cfg *config.Config
+ id string
+ nodeID int64
+ grpcClient Ydb_Query_V1.QueryServiceClient
+ statusCode statusCode
+ closeOnce func(ctx context.Context) error
+ checks []func(s *Session) bool
+ }
+ sessionOption func(session *Session)
+)
+
+func withSessionCheck(f func(*Session) bool) sessionOption {
+ return func(s *Session) {
+ s.checks = append(s.checks, f)
+ }
+}
+
+func createSession(
+ ctx context.Context, client Ydb_Query_V1.QueryServiceClient, cfg *config.Config, opts ...sessionOption,
+) (s *Session, finalErr error) {
+ s = &Session{
+ cfg: cfg,
+ grpcClient: client,
+ statusCode: statusUnknown,
+ checks: []func(*Session) bool{
+ func(s *Session) bool {
+ switch s.status() {
+ case statusIdle, statusInUse:
+ return true
+ default:
+ return false
+ }
+ },
+ },
+ }
+ defer func() {
+ if finalErr != nil && s != nil {
+ s.setStatus(statusError)
+ }
+ }()
+
+ for _, opt := range opts {
+ opt(s)
+ }
+
+ onDone := trace.QueryOnSessionCreate(s.cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.createSession"),
+ )
+ defer func() {
+ onDone(s, finalErr)
+ }()
+
+ response, err := client.CreateSession(ctx, &Ydb_Query.CreateSessionRequest{})
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ defer func() {
+ if finalErr != nil {
+ _ = deleteSession(ctx, client, response.GetSessionId())
+ }
+ }()
+
+ s.id = response.GetSessionId()
+ s.nodeID = response.GetNodeId()
+
+ err = s.attach(ctx)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ s.setStatus(statusIdle)
+
+ return s, nil
+}
+
+func (s *Session) attach(ctx context.Context) (finalErr error) {
+ onDone := trace.QueryOnSessionAttach(s.cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*Session).attach"), s)
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ attachCtx, cancelAttach := xcontext.WithCancel(xcontext.ValueOnly(ctx))
+
+ attach, err := s.grpcClient.AttachSession(attachCtx, &Ydb_Query.AttachSessionRequest{
+ SessionId: s.id,
+ })
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ _, err = attach.Recv()
+ if err != nil {
+ cancelAttach()
+
+ return xerrors.WithStackTrace(err)
+ }
+
+ s.closeOnce = xsync.OnceFunc(func(ctx context.Context) (err error) {
+ defer cancelAttach()
+
+ s.setStatus(statusClosing)
+ defer s.setStatus(statusClosed)
+
+ var cancel context.CancelFunc
+ if d := s.cfg.SessionDeleteTimeout(); d > 0 {
+ ctx, cancel = xcontext.WithTimeout(ctx, d)
+ } else {
+ ctx, cancel = xcontext.WithCancel(ctx)
+ }
+ defer cancel()
+
+ if err = deleteSession(ctx, s.grpcClient, s.id); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+ })
+
+ go func() {
+ defer func() {
+ _ = s.closeOnce(xcontext.ValueOnly(ctx))
+ }()
+
+ for {
+ if !s.IsAlive() {
+ return
+ }
+ _, recvErr := attach.Recv()
+ if recvErr != nil {
+ if xerrors.Is(recvErr, io.EOF) {
+ s.setStatus(statusClosed)
+ } else {
+ s.setStatus(statusError)
+ }
+
+ return
+ }
+ }
+ }()
+
+ return nil
+}
+
+func deleteSession(ctx context.Context, client Ydb_Query_V1.QueryServiceClient, sessionID string) error {
+ _, err := client.DeleteSession(ctx,
+ &Ydb_Query.DeleteSessionRequest{
+ SessionId: sessionID,
+ },
+ )
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+}
+
+func (s *Session) IsAlive() bool {
+ for _, check := range s.checks {
+ if !check(s) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (s *Session) Close(ctx context.Context) (err error) {
+ onDone := trace.QueryOnSessionDelete(s.cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*Session).Close"), s)
+ defer func() {
+ onDone(err)
+ }()
+
+ if s.closeOnce != nil {
+ return s.closeOnce(ctx)
+ }
+
+ return nil
+}
+
+func begin(
+ ctx context.Context,
+ client Ydb_Query_V1.QueryServiceClient,
+ s *Session,
+ txSettings query.TransactionSettings,
+) (*transaction, error) {
+ a := allocator.New()
+ defer a.Free()
+ response, err := client.BeginTransaction(ctx,
+ &Ydb_Query.BeginTransactionRequest{
+ SessionId: s.id,
+ TxSettings: txSettings.ToYDB(a),
+ },
+ )
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return newTransaction(response.GetTxMeta().GetId(), s), nil
+}
+
+func (s *Session) Begin(
+ ctx context.Context,
+ txSettings query.TransactionSettings,
+) (
+ _ query.Transaction, err error,
+) {
+ var tx *transaction
+
+ onDone := trace.QueryOnSessionBegin(s.cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*Session).Begin"), s)
+ defer func() {
+ onDone(err, tx)
+ }()
+
+ tx, err = begin(ctx, s.grpcClient, s, txSettings)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+ tx.s = s
+
+ return tx, nil
+}
+
+func (s *Session) ID() string {
+ return s.id
+}
+
+func (s *Session) NodeID() int64 {
+ return s.nodeID
+}
+
+func (s *Session) status() statusCode {
+ return statusCode(atomic.LoadUint32((*uint32)(&s.statusCode)))
+}
+
+func (s *Session) setStatus(code statusCode) {
+ atomic.StoreUint32((*uint32)(&s.statusCode), uint32(code))
+}
+
+func (s *Session) Status() string {
+ return s.status().String()
+}
+
+func (s *Session) Execute(
+ ctx context.Context, q string, opts ...options.ExecuteOption,
+) (_ query.Transaction, _ query.Result, err error) {
+ onDone := trace.QueryOnSessionExecute(s.cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.(*Session).Execute"), s, q)
+ defer func() {
+ onDone(err)
+ }()
+
+ tx, r, err := execute(ctx, s, s.grpcClient, q, options.ExecuteSettings(opts...))
+ if err != nil {
+ return nil, nil, xerrors.WithStackTrace(err)
+ }
+
+ return tx, r, nil
+}
diff --git a/internal/query/session_status.go b/internal/query/session_status.go
new file mode 100644
index 000000000..134ade32d
--- /dev/null
+++ b/internal/query/session_status.go
@@ -0,0 +1,37 @@
+package query
+
+import (
+ "fmt"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/session"
+)
+
+type statusCode uint32
+
+const (
+ statusUnknown = statusCode(iota)
+ statusIdle
+ statusInUse
+ statusClosing
+ statusClosed
+ statusError
+)
+
+func (s statusCode) String() string {
+ switch s {
+ case statusUnknown:
+ return session.StatusUnknown
+ case statusIdle:
+ return session.StatusIdle
+ case statusInUse:
+ return session.StatusInUse
+ case statusClosing:
+ return session.StatusClosing
+ case statusClosed:
+ return session.StatusClosed
+ case statusError:
+ return session.StatusError
+ default:
+ return fmt.Sprintf("Unknown%d", s)
+ }
+}
diff --git a/internal/query/session_test.go b/internal/query/session_test.go
new file mode 100644
index 000000000..b75faf5d7
--- /dev/null
+++ b/internal/query/session_test.go
@@ -0,0 +1,56 @@
+package query
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "go.uber.org/mock/gomock"
+ grpcCodes "google.golang.org/grpc/codes"
+ grpcStatus "google.golang.org/grpc/status"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+)
+
+func TestBegin(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).Return(&Ydb_Query.BeginTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ TxMeta: &Ydb_Query.TransactionMeta{
+ Id: "123",
+ },
+ }, nil)
+ t.Log("begin")
+ tx, err := begin(ctx, service, &Session{id: "123"}, query.TxSettings())
+ require.NoError(t, err)
+ require.Equal(t, "123", tx.id)
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).Return(nil, grpcStatus.Error(grpcCodes.Unavailable, ""))
+ t.Log("begin")
+ _, err := begin(ctx, service, &Session{id: "123"}, query.TxSettings())
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().BeginTransaction(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ t.Log("begin")
+ _, err := begin(ctx, service, &Session{id: "123"}, query.TxSettings())
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ })
+}
diff --git a/internal/query/transaction.go b/internal/query/transaction.go
new file mode 100644
index 000000000..fbfcd9151
--- /dev/null
+++ b/internal/query/transaction.go
@@ -0,0 +1,81 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+var _ query.Transaction = (*transaction)(nil)
+
+type transaction struct {
+ id string
+ s *Session
+}
+
+func newTransaction(id string, s *Session) *transaction {
+ return &transaction{
+ id: id,
+ s: s,
+ }
+}
+
+func (tx transaction) ID() string {
+ return tx.id
+}
+
+func (tx transaction) Execute(ctx context.Context, q string, opts ...options.TxExecuteOption) (
+ r query.Result, finalErr error,
+) {
+ onDone := trace.QueryOnTxExecute(tx.s.cfg.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/query.transaction.Execute"), tx.s, tx, q)
+ defer func() {
+ onDone(finalErr)
+ }()
+
+ _, res, err := execute(ctx, tx.s, tx.s.grpcClient, q, options.TxExecuteSettings(tx.id, opts...).ExecuteSettings)
+ if err != nil {
+ return nil, xerrors.WithStackTrace(err)
+ }
+
+ return res, nil
+}
+
+func commitTx(ctx context.Context, client Ydb_Query_V1.QueryServiceClient, sessionID, txID string) error {
+ _, err := client.CommitTransaction(ctx, &Ydb_Query.CommitTransactionRequest{
+ SessionId: sessionID,
+ TxId: txID,
+ })
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+}
+
+func (tx transaction) CommitTx(ctx context.Context) (err error) {
+ return commitTx(ctx, tx.s.grpcClient, tx.s.id, tx.id)
+}
+
+func rollback(ctx context.Context, client Ydb_Query_V1.QueryServiceClient, sessionID, txID string) error {
+ _, err := client.RollbackTransaction(ctx, &Ydb_Query.RollbackTransactionRequest{
+ SessionId: sessionID,
+ TxId: txID,
+ })
+ if err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+}
+
+func (tx transaction) Rollback(ctx context.Context) (err error) {
+ return rollback(ctx, tx.s.grpcClient, tx.s.id, tx.id)
+}
diff --git a/internal/query/transaction_test.go b/internal/query/transaction_test.go
new file mode 100644
index 000000000..83ecdfc6c
--- /dev/null
+++ b/internal/query/transaction_test.go
@@ -0,0 +1,233 @@
+package query
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+ "go.uber.org/mock/gomock"
+ "google.golang.org/grpc"
+ grpcCodes "google.golang.org/grpc/codes"
+ grpcStatus "google.golang.org/grpc/status"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+)
+
+func TestCommitTx(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(
+ &Ydb_Query.CommitTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil,
+ )
+ t.Log("commit")
+ err := commitTx(ctx, service, "123", "456")
+ require.NoError(t, err)
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(
+ nil, grpcStatus.Error(grpcCodes.Unavailable, ""),
+ )
+ t.Log("commit")
+ err := commitTx(ctx, service, "123", "456")
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().CommitTransaction(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ t.Log("commit")
+ err := commitTx(ctx, service, "123", "456")
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ })
+}
+
+func TestRollback(t *testing.T) {
+ t.Run("HappyWay", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).Return(
+ &Ydb_Query.RollbackTransactionResponse{
+ Status: Ydb.StatusIds_SUCCESS,
+ }, nil,
+ )
+ t.Log("rollback")
+ err := rollback(ctx, service, "123", "456")
+ require.NoError(t, err)
+ })
+ t.Run("TransportError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).Return(
+ nil, grpcStatus.Error(grpcCodes.Unavailable, ""),
+ )
+ t.Log("rollback")
+ err := rollback(ctx, service, "123", "456")
+ require.Error(t, err)
+ require.True(t, xerrors.IsTransportError(err, grpcCodes.Unavailable))
+ })
+ t.Run("OperationError", func(t *testing.T) {
+ ctx := xtest.Context(t)
+ ctrl := gomock.NewController(t)
+ service := NewMockQueryServiceClient(ctrl)
+ service.EXPECT().RollbackTransaction(gomock.Any(), gomock.Any()).Return(nil,
+ xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_UNAVAILABLE)),
+ )
+ t.Log("rollback")
+ err := rollback(ctx, service, "123", "456")
+ require.Error(t, err)
+ require.True(t, xerrors.IsOperationError(err, Ydb.StatusIds_UNAVAILABLE))
+ })
+}
+
+type testExecuteSettings struct {
+ execMode options.ExecMode
+ statsMode options.StatsMode
+ txControl *query.TransactionControl
+ syntax options.Syntax
+ params *params.Parameters
+ callOptions []grpc.CallOption
+}
+
+func (s testExecuteSettings) ExecMode() options.ExecMode {
+ return s.execMode
+}
+
+func (s testExecuteSettings) StatsMode() options.StatsMode {
+ return s.statsMode
+}
+
+func (s testExecuteSettings) TxControl() *query.TransactionControl {
+ return s.txControl
+}
+
+func (s testExecuteSettings) Syntax() options.Syntax {
+ return s.syntax
+}
+
+func (s testExecuteSettings) Params() *params.Parameters {
+ return s.params
+}
+
+func (s testExecuteSettings) CallOptions() []grpc.CallOption {
+ return s.callOptions
+}
+
+var _ executeConfig = testExecuteSettings{}
+
+func TestTxExecuteSettings(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ txID string
+ txOpts []options.TxExecuteOption
+ settings executeConfig
+ }{
+ {
+ name: "WithTxID",
+ txID: "test",
+ txOpts: nil,
+ settings: testExecuteSettings{
+ execMode: options.ExecModeExecute,
+ statsMode: options.StatsModeNone,
+ txControl: query.TxControl(query.WithTxID("test")),
+ syntax: options.SyntaxYQL,
+ },
+ },
+ {
+ name: "WithStats",
+ txOpts: []options.TxExecuteOption{
+ options.WithStatsMode(options.StatsModeFull),
+ },
+ settings: testExecuteSettings{
+ execMode: options.ExecModeExecute,
+ statsMode: options.StatsModeFull,
+ txControl: query.TxControl(query.WithTxID("")),
+ syntax: options.SyntaxYQL,
+ },
+ },
+ {
+ name: "WithExecMode",
+ txOpts: []options.TxExecuteOption{
+ options.WithExecMode(options.ExecModeExplain),
+ },
+ settings: testExecuteSettings{
+ execMode: options.ExecModeExplain,
+ statsMode: options.StatsModeNone,
+ txControl: query.TxControl(query.WithTxID("")),
+ syntax: options.SyntaxYQL,
+ },
+ },
+ {
+ name: "WithSyntax",
+ txOpts: []options.TxExecuteOption{
+ options.WithSyntax(options.SyntaxPostgreSQL),
+ },
+ settings: testExecuteSettings{
+ execMode: options.ExecModeExecute,
+ statsMode: options.StatsModeNone,
+ txControl: query.TxControl(query.WithTxID("")),
+ syntax: options.SyntaxPostgreSQL,
+ },
+ },
+ {
+ name: "WithGrpcOptions",
+ txOpts: []options.TxExecuteOption{
+ options.WithCallOptions(grpc.CallContentSubtype("test")),
+ },
+ settings: testExecuteSettings{
+ execMode: options.ExecModeExecute,
+ statsMode: options.StatsModeNone,
+ txControl: query.TxControl(query.WithTxID("")),
+ syntax: options.SyntaxYQL,
+ callOptions: []grpc.CallOption{
+ grpc.CallContentSubtype("test"),
+ },
+ },
+ },
+ {
+ name: "WithParams",
+ txOpts: []options.TxExecuteOption{
+ options.WithParameters(
+ params.Builder{}.Param("$a").Text("A").Build(),
+ ),
+ },
+ settings: testExecuteSettings{
+ execMode: options.ExecModeExecute,
+ statsMode: options.StatsModeNone,
+ txControl: query.TxControl(query.WithTxID("")),
+ syntax: options.SyntaxYQL,
+ params: params.Builder{}.Param("$a").Text("A").Build(),
+ },
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ a := allocator.New()
+ settings := options.TxExecuteSettings(tt.txID, tt.txOpts...).ExecuteSettings
+ require.Equal(t, tt.settings.Syntax(), settings.Syntax())
+ require.Equal(t, tt.settings.ExecMode(), settings.ExecMode())
+ require.Equal(t, tt.settings.StatsMode(), settings.StatsMode())
+ require.Equal(t, tt.settings.TxControl().ToYDB(a).String(), settings.TxControl().ToYDB(a).String())
+ require.Equal(t, tt.settings.Params().ToYDB(a), settings.Params().ToYDB(a))
+ require.Equal(t, tt.settings.CallOptions(), settings.CallOptions())
+ })
+ }
+}
diff --git a/internal/query/tx/control.go b/internal/query/tx/control.go
new file mode 100644
index 000000000..a5be2fb21
--- /dev/null
+++ b/internal/query/tx/control.go
@@ -0,0 +1,162 @@
+package tx
+
+import (
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+)
+
+var (
+ _ interface {
+ ToYDB(a *allocator.Allocator) *Ydb_Query.TransactionControl
+ } = (*Control)(nil)
+ _ Selector = (*Settings)(nil)
+)
+
+type (
+ Selector interface {
+ applyTxSelector(a *allocator.Allocator, txControl *Ydb_Query.TransactionControl)
+ }
+ ControlOption interface {
+ applyTxControlOption(txControl *Control)
+ }
+ Control struct {
+ selector Selector
+ commit bool
+ }
+ Identifier interface {
+ ID() string
+ }
+)
+
+func (ctrl *Control) ToYDB(a *allocator.Allocator) *Ydb_Query.TransactionControl {
+ if ctrl == nil {
+ return nil
+ }
+
+ txControl := a.QueryTransactionControl()
+ ctrl.selector.applyTxSelector(a, txControl)
+ txControl.CommitTx = ctrl.commit
+
+ return txControl
+}
+
+var (
+ _ ControlOption = beginTxOptions{}
+ _ Selector = beginTxOptions{}
+)
+
+type beginTxOptions []Option
+
+func (opts beginTxOptions) applyTxControlOption(txControl *Control) {
+ txControl.selector = opts
+}
+
+func (opts beginTxOptions) applyTxSelector(a *allocator.Allocator, txControl *Ydb_Query.TransactionControl) {
+ selector := a.QueryTransactionControlBeginTx()
+ selector.BeginTx = a.QueryTransactionSettings()
+ for _, opt := range opts {
+ if opt != nil {
+ opt.ApplyTxSettingsOption(a, selector.BeginTx)
+ }
+ }
+ txControl.TxSelector = selector
+}
+
+// BeginTx returns selector transaction control option
+func BeginTx(opts ...Option) beginTxOptions {
+ return opts
+}
+
+var (
+ _ ControlOption = txIDTxControlOption("")
+ _ Selector = txIDTxControlOption("")
+)
+
+type txIDTxControlOption string
+
+func (id txIDTxControlOption) applyTxControlOption(txControl *Control) {
+ txControl.selector = id
+}
+
+func (id txIDTxControlOption) applyTxSelector(a *allocator.Allocator, txControl *Ydb_Query.TransactionControl) {
+ selector := a.QueryTransactionControlTxID()
+ selector.TxId = string(id)
+ txControl.TxSelector = selector
+}
+
+func WithTx(t Identifier) txIDTxControlOption {
+ return txIDTxControlOption(t.ID())
+}
+
+func WithTxID(txID string) txIDTxControlOption {
+ return txIDTxControlOption(txID)
+}
+
+type commitTxOption struct{}
+
+func (c commitTxOption) applyTxControlOption(txControl *Control) {
+ txControl.commit = true
+}
+
+// CommitTx returns commit transaction control option
+func CommitTx() ControlOption {
+ return commitTxOption{}
+}
+
+// NewControl makes transaction control from given options
+func NewControl(opts ...ControlOption) *Control {
+ txControl := &Control{
+ selector: BeginTx(WithSerializableReadWrite()),
+ commit: false,
+ }
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyTxControlOption(txControl)
+ }
+ }
+
+ return txControl
+}
+
+func NoTx() *Control {
+ return nil
+}
+
+// DefaultTxControl returns default transaction control with serializable read-write isolation mode and auto-commit
+func DefaultTxControl() *Control {
+ return NoTx()
+}
+
+// SerializableReadWriteTxControl returns transaction control with serializable read-write isolation mode
+func SerializableReadWriteTxControl(opts ...ControlOption) *Control {
+ return NewControl(
+ append([]ControlOption{
+ BeginTx(WithSerializableReadWrite()),
+ }, opts...)...,
+ )
+}
+
+// OnlineReadOnlyTxControl returns online read-only transaction control
+func OnlineReadOnlyTxControl(opts ...OnlineReadOnlyOption) *Control {
+ return NewControl(
+ BeginTx(WithOnlineReadOnly(opts...)),
+ CommitTx(), // open transactions not supported for OnlineReadOnly
+ )
+}
+
+// StaleReadOnlyTxControl returns stale read-only transaction control
+func StaleReadOnlyTxControl() *Control {
+ return NewControl(
+ BeginTx(WithStaleReadOnly()),
+ CommitTx(), // open transactions not supported for StaleReadOnly
+ )
+}
+
+// SnapshotReadOnlyTxControl returns snapshot read-only transaction control
+func SnapshotReadOnlyTxControl() *Control {
+ return NewControl(
+ BeginTx(WithSnapshotReadOnly()),
+ CommitTx(), // open transactions not supported for StaleReadOnly
+ )
+}
diff --git a/internal/query/tx/settings.go b/internal/query/tx/settings.go
new file mode 100644
index 000000000..ac051d132
--- /dev/null
+++ b/internal/query/tx/settings.go
@@ -0,0 +1,149 @@
+package tx
+
+import (
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Query"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+)
+
+var (
+ serializableReadWrite = &Ydb_Query.TransactionSettings_SerializableReadWrite{
+ SerializableReadWrite: &Ydb_Query.SerializableModeSettings{},
+ }
+ staleReadOnly = &Ydb_Query.TransactionSettings_StaleReadOnly{
+ StaleReadOnly: &Ydb_Query.StaleModeSettings{},
+ }
+ snapshotReadOnly = &Ydb_Query.TransactionSettings_SnapshotReadOnly{
+ SnapshotReadOnly: &Ydb_Query.SnapshotModeSettings{},
+ }
+ onlineReadOnlyAllowInconsistentReads = &Ydb_Query.TransactionSettings_OnlineReadOnly{
+ OnlineReadOnly: &Ydb_Query.OnlineModeSettings{AllowInconsistentReads: true},
+ }
+ onlineReadOnlyForbidInconsistentReads = &Ydb_Query.TransactionSettings_OnlineReadOnly{
+ OnlineReadOnly: &Ydb_Query.OnlineModeSettings{AllowInconsistentReads: false},
+ }
+)
+
+// Transaction settings options
+type (
+ Option interface {
+ ApplyTxSettingsOption(a *allocator.Allocator, txSettings *Ydb_Query.TransactionSettings)
+ }
+ Settings []Option
+)
+
+func (opts Settings) applyTxSelector(a *allocator.Allocator, txControl *Ydb_Query.TransactionControl) {
+ beginTx := a.QueryTransactionControlBeginTx()
+ beginTx.BeginTx = a.QueryTransactionSettings()
+ for _, opt := range opts {
+ if opt != nil {
+ opt.ApplyTxSettingsOption(a, beginTx.BeginTx)
+ }
+ }
+ txControl.TxSelector = beginTx
+}
+
+func (opts Settings) ToYDB(a *allocator.Allocator) *Ydb_Query.TransactionSettings {
+ txSettings := a.QueryTransactionSettings()
+ for _, opt := range opts {
+ if opt != nil {
+ opt.ApplyTxSettingsOption(a, txSettings)
+ }
+ }
+
+ return txSettings
+}
+
+// NewSettings returns transaction settings
+func NewSettings(opts ...Option) Settings {
+ return opts
+}
+
+func WithDefaultTxMode() Option {
+ return WithSerializableReadWrite()
+}
+
+var _ Option = serializableReadWriteTxSettingsOption{}
+
+type serializableReadWriteTxSettingsOption struct{}
+
+func (o serializableReadWriteTxSettingsOption) ApplyTxSettingsOption(
+ a *allocator.Allocator, txSettings *Ydb_Query.TransactionSettings,
+) {
+ txSettings.TxMode = serializableReadWrite
+}
+
+func WithSerializableReadWrite() Option {
+ return serializableReadWriteTxSettingsOption{}
+}
+
+var _ Option = snapshotReadOnlyTxSettingsOption{}
+
+type snapshotReadOnlyTxSettingsOption struct{}
+
+func (snapshotReadOnlyTxSettingsOption) ApplyTxSettingsOption(
+ a *allocator.Allocator, settings *Ydb_Query.TransactionSettings,
+) {
+ settings.TxMode = snapshotReadOnly
+}
+
+func WithSnapshotReadOnly() Option {
+ return snapshotReadOnlyTxSettingsOption{}
+}
+
+var _ Option = staleReadOnlySettingsOption{}
+
+type staleReadOnlySettingsOption struct{}
+
+func (staleReadOnlySettingsOption) ApplyTxSettingsOption(
+ a *allocator.Allocator, settings *Ydb_Query.TransactionSettings,
+) {
+ settings.TxMode = staleReadOnly
+}
+
+func WithStaleReadOnly() Option {
+ return staleReadOnlySettingsOption{}
+}
+
+type (
+ onlineReadOnly bool
+ OnlineReadOnlyOption interface {
+ applyTxOnlineReadOnlyOption(opt *onlineReadOnly)
+ }
+)
+
+var _ OnlineReadOnlyOption = inconsistentReadsTxOnlineReadOnlyOption{}
+
+type inconsistentReadsTxOnlineReadOnlyOption struct{}
+
+func (i inconsistentReadsTxOnlineReadOnlyOption) applyTxOnlineReadOnlyOption(b *onlineReadOnly) {
+ *b = true
+}
+
+func WithInconsistentReads() OnlineReadOnlyOption {
+ return inconsistentReadsTxOnlineReadOnlyOption{}
+}
+
+var _ Option = onlineReadOnlySettingsOption{}
+
+type onlineReadOnlySettingsOption []OnlineReadOnlyOption
+
+func (opts onlineReadOnlySettingsOption) ApplyTxSettingsOption(
+ a *allocator.Allocator, settings *Ydb_Query.TransactionSettings,
+) {
+ var ro onlineReadOnly
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyTxOnlineReadOnlyOption(&ro)
+ }
+ }
+ if ro {
+ settings.TxMode = onlineReadOnlyAllowInconsistentReads
+ } else {
+ settings.TxMode = onlineReadOnlyForbidInconsistentReads
+ }
+}
+
+func WithOnlineReadOnly(opts ...OnlineReadOnlyOption) onlineReadOnlySettingsOption {
+ return opts
+}
diff --git a/internal/ratelimiter/client.go b/internal/ratelimiter/client.go
index 206cbf5f9..eec07b118 100644
--- a/internal/ratelimiter/client.go
+++ b/internal/ratelimiter/client.go
@@ -37,11 +37,11 @@ func (c *Client) Close(ctx context.Context) error {
return nil
}
-func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) (*Client, error) {
+func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client {
return &Client{
config: config,
service: Ydb_RateLimiter_V1.NewRateLimiterServiceClient(cc),
- }, nil
+ }
}
func (c *Client) CreateResource(
diff --git a/internal/ratelimiter/config/config.go b/internal/ratelimiter/config/config.go
index a3f2f48e6..d0761f779 100644
--- a/internal/ratelimiter/config/config.go
+++ b/internal/ratelimiter/config/config.go
@@ -6,8 +6,6 @@ import (
)
// Config is a configuration of ratelimiter client
-//
-//nolint:maligned
type Config struct {
config.Common
@@ -39,9 +37,9 @@ func New(opts ...Option) Config {
c := Config{
trace: &trace.Ratelimiter{},
}
- for _, o := range opts {
- if o != nil {
- o(&c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&c)
}
}
diff --git a/internal/ratelimiter/options/acquire.go b/internal/ratelimiter/options/acquire.go
index d1f91d64f..1febf3e63 100644
--- a/internal/ratelimiter/options/acquire.go
+++ b/internal/ratelimiter/options/acquire.go
@@ -74,9 +74,9 @@ func NewAcquire(opts ...AcquireOption) Acquire {
h := &acquireOptionsHolder{
acquireType: AcquireTypeDefault,
}
- for _, o := range opts {
- if o != nil {
- o(h)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(h)
}
}
diff --git a/internal/repeater/repeater.go b/internal/repeater/repeater.go
index fad8da8c1..ec75be3c1 100644
--- a/internal/repeater/repeater.go
+++ b/internal/repeater/repeater.go
@@ -108,9 +108,9 @@ func New(
trace: &trace.Driver{},
}
- for _, o := range opts {
- if o != nil {
- o(r)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(r)
}
}
@@ -147,7 +147,7 @@ func (r *repeater) wakeUp(ctx context.Context, e Event) (err error) {
ctx = WithEvent(ctx, e)
onDone := trace.DriverOnRepeaterWakeUp(r.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/repeater.(*repeater).wakeUp"),
r.name, e,
)
defer func() {
diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go
new file mode 100644
index 000000000..19c91de52
--- /dev/null
+++ b/internal/scanner/scanner.go
@@ -0,0 +1,145 @@
+package scanner
+
+import (
+ "io"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/decimal"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+)
+
+// RawValue scanning non-primitive yql types or for own implementation scanner native API
+type RawValue interface {
+ Path() string
+ WritePathTo(w io.Writer) (n int64, err error)
+ Type() types.Type
+ Bool() (v bool)
+ Int8() (v int8)
+ Uint8() (v uint8)
+ Int16() (v int16)
+ Uint16() (v uint16)
+ Int32() (v int32)
+ Uint32() (v uint32)
+ Int64() (v int64)
+ Uint64() (v uint64)
+ Float() (v float32)
+ Double() (v float64)
+ Date() (v time.Time)
+ Datetime() (v time.Time)
+ Timestamp() (v time.Time)
+ Interval() (v time.Duration)
+ TzDate() (v time.Time)
+ TzDatetime() (v time.Time)
+ TzTimestamp() (v time.Time)
+ String() (v []byte)
+ UTF8() (v string)
+ YSON() (v []byte)
+ JSON() (v []byte)
+ UUID() (v [16]byte)
+ JSONDocument() (v []byte)
+ DyNumber() (v string)
+ Value() value.Value
+
+ // Any returns any primitive or optional value.
+ // Currently, it may return one of these types:
+ //
+ // bool
+ // int8
+ // uint8
+ // int16
+ // uint16
+ // int32
+ // uint32
+ // int64
+ // uint64
+ // float32
+ // float64
+ // []byte
+ // string
+ // [16]byte
+ //
+ Any() interface{}
+
+ // Unwrap unwraps current item under scan interpreting it as Optional types.
+ Unwrap()
+ AssertType(t types.Type) bool
+ IsNull() bool
+ IsOptional() bool
+
+ // ListIn interprets current item under scan as a ydb's list.
+ // It returns the size of the nested items.
+ // If current item under scan is not a list types, it returns -1.
+ ListIn() (size int)
+
+ // ListItem selects current item i-th element as an item to scan.
+ // ListIn() must be called before.
+ ListItem(i int)
+
+ // ListOut leaves list entered before by ListIn() call.
+ ListOut()
+
+ // TupleIn interprets current item under scan as a ydb's tuple.
+ // It returns the size of the nested items.
+ TupleIn() (size int)
+
+ // TupleItem selects current item i-th element as an item to scan.
+ // Note that TupleIn() must be called before.
+ // It panics if it is out of bounds.
+ TupleItem(i int)
+
+ // TupleOut leaves tuple entered before by TupleIn() call.
+ TupleOut()
+
+ // StructIn interprets current item under scan as a ydb's struct.
+ // It returns the size of the nested items – the struct fields values.
+ // If there is no current item under scan it returns -1.
+ StructIn() (size int)
+
+ // StructField selects current item i-th field value as an item to scan.
+ // Note that StructIn() must be called before.
+ // It panics if i is out of bounds.
+ StructField(i int) (name string)
+
+ // StructOut leaves struct entered before by StructIn() call.
+ StructOut()
+
+ // DictIn interprets current item under scan as a ydb's dict.
+ // It returns the size of the nested items pairs.
+ // If there is no current item under scan it returns -1.
+ DictIn() (size int)
+
+ // DictKey selects current item i-th pair key as an item to scan.
+ // Note that DictIn() must be called before.
+ // It panics if i is out of bounds.
+ DictKey(i int)
+
+ // DictPayload selects current item i-th pair value as an item to scan.
+ // Note that DictIn() must be called before.
+ // It panics if i is out of bounds.
+ DictPayload(i int)
+
+ // DictOut leaves dict entered before by DictIn() call.
+ DictOut()
+
+ // Variant unwraps current item under scan interpreting it as Variant types.
+ // It returns non-empty name of a field that is filled for struct-based
+ // variant.
+ // It always returns an index of filled field of a Type.
+ Variant() (name string, index uint32)
+
+ // Decimal returns decimal value represented by big-endian 128 bit signed integer.
+ Decimal(t types.Type) (v [16]byte)
+
+ // UnwrapDecimal returns decimal value represented by big-endian 128 bit signed
+ // integer and its types information.
+ UnwrapDecimal() decimal.Decimal
+ IsDecimal() bool
+ Err() error
+}
+
+// Scanner scanning raw ydb types
+type Scanner interface {
+ // UnmarshalYDB must be implemented on client-side for unmarshal raw ydb value.
+ UnmarshalYDB(raw RawValue) error
+}
diff --git a/internal/scheme/client.go b/internal/scheme/client.go
index f9dd3a621..6f46ed736 100644
--- a/internal/scheme/client.go
+++ b/internal/scheme/client.go
@@ -38,16 +38,16 @@ func (c *Client) Close(_ context.Context) error {
return nil
}
-func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) (*Client, error) {
+func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client {
return &Client{
config: config,
service: Ydb_Scheme_V1.NewSchemeServiceClient(cc),
- }, nil
+ }
}
func (c *Client) MakeDirectory(ctx context.Context, path string) (finalErr error) {
onDone := trace.SchemeOnMakeDirectory(c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scheme.(*Client).MakeDirectory"),
path,
)
defer func() {
@@ -86,7 +86,7 @@ func (c *Client) makeDirectory(ctx context.Context, path string) (err error) {
func (c *Client) RemoveDirectory(ctx context.Context, path string) (finalErr error) {
onDone := trace.SchemeOnRemoveDirectory(c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scheme.(*Client).RemoveDirectory"),
path,
)
defer func() {
@@ -124,7 +124,9 @@ func (c *Client) removeDirectory(ctx context.Context, path string) (err error) {
}
func (c *Client) ListDirectory(ctx context.Context, path string) (d scheme.Directory, finalErr error) {
- onDone := trace.SchemeOnListDirectory(c.config.Trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.SchemeOnListDirectory(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scheme.(*Client).ListDirectory"),
+ )
defer func() {
onDone(finalErr)
}()
@@ -173,16 +175,16 @@ func (c *Client) listDirectory(ctx context.Context, path string) (scheme.Directo
if err != nil {
return d, xerrors.WithStackTrace(err)
}
- d.From(result.Self)
- d.Children = make([]scheme.Entry, len(result.Children))
- putEntry(d.Children, result.Children)
+ d.From(result.GetSelf())
+ d.Children = make([]scheme.Entry, len(result.GetChildren()))
+ putEntry(d.Children, result.GetChildren())
return d, nil
}
func (c *Client) DescribePath(ctx context.Context, path string) (e scheme.Entry, finalErr error) {
onDone := trace.SchemeOnDescribePath(c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scheme.(*Client).DescribePath"),
path,
)
defer func() {
@@ -234,7 +236,7 @@ func (c *Client) describePath(ctx context.Context, path string) (e scheme.Entry,
if err != nil {
return e, xerrors.WithStackTrace(err)
}
- e.From(result.Self)
+ e.From(result.GetSelf())
return e, nil
}
@@ -243,16 +245,16 @@ func (c *Client) ModifyPermissions(
ctx context.Context, path string, opts ...scheme.PermissionsOption,
) (finalErr error) {
onDone := trace.SchemeOnModifyPermissions(c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scheme.(*Client).ModifyPermissions"),
path,
)
defer func() {
onDone(finalErr)
}()
var desc permissionsDesc
- for _, o := range opts {
- if o != nil {
- o(&desc)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&desc)
}
}
call := func(ctx context.Context) error {
diff --git a/internal/scheme/config/config.go b/internal/scheme/config/config.go
index 87677eb48..a684fc83e 100644
--- a/internal/scheme/config/config.go
+++ b/internal/scheme/config/config.go
@@ -6,8 +6,6 @@ import (
)
// Config is a configuration of scheme client
-//
-//nolint:maligned
type Config struct {
config.Common
@@ -52,9 +50,9 @@ func New(opts ...Option) Config {
c := Config{
trace: &trace.Scheme{},
}
- for _, o := range opts {
- if o != nil {
- o(&c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&c)
}
}
diff --git a/internal/scheme/options_test.go b/internal/scheme/options_test.go
index 5f99dfd32..b8ef769e9 100644
--- a/internal/scheme/options_test.go
+++ b/internal/scheme/options_test.go
@@ -28,9 +28,9 @@ func TestSchemeOptions(t *testing.T) {
}
var desc permissionsDesc
- for _, o := range opts {
- if o != nil {
- o(&desc)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&desc)
}
}
@@ -40,7 +40,7 @@ func TestSchemeOptions(t *testing.T) {
count := len(desc.actions)
for _, a := range desc.actions {
- switch a := a.Action.(type) {
+ switch a := a.GetAction().(type) {
case *Ydb_Scheme.PermissionsAction_ChangeOwner:
count--
if a.ChangeOwner != "ow" {
@@ -48,17 +48,19 @@ func TestSchemeOptions(t *testing.T) {
}
case *Ydb_Scheme.PermissionsAction_Grant:
count--
- if a.Grant.Subject != "grant" || len(a.Grant.PermissionNames) != 3 {
+ if a.Grant.GetSubject() != "grant" || len(a.Grant.GetPermissionNames()) != 3 {
t.Errorf("Grant is not as expected")
}
case *Ydb_Scheme.PermissionsAction_Set:
count--
- if a.Set.Subject != "set" || len(a.Set.PermissionNames) != 1 || a.Set.PermissionNames[0] != "d" {
+ if a.Set.GetSubject() != "set" || len(a.Set.GetPermissionNames()) != 1 || a.Set.GetPermissionNames()[0] != "d" {
t.Errorf("Set is not as expected")
}
case *Ydb_Scheme.PermissionsAction_Revoke:
count--
- if a.Revoke.Subject != "revoke" || len(a.Revoke.PermissionNames) != 1 || a.Revoke.PermissionNames[0] != "e" {
+ revokeSubject := a.Revoke.GetSubject()
+ permissionNames := a.Revoke.GetPermissionNames()
+ if revokeSubject != "revoke" || len(permissionNames) != 1 || permissionNames[0] != "e" {
t.Errorf("Revoke is not as expected")
}
}
diff --git a/internal/scripting/client.go b/internal/scripting/client.go
index bf57a3edd..a8ecc18bb 100644
--- a/internal/scripting/client.go
+++ b/internal/scripting/client.go
@@ -12,17 +12,17 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
"github.com/ydb-platform/ydb-go-sdk/v3/scripting"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
@@ -32,21 +32,23 @@ var (
errNilClient = xerrors.Wrap(errors.New("scripting client is not initialized"))
)
-type Client struct {
- config config.Config
- service Ydb_Scripting_V1.ScriptingServiceClient
-}
+type (
+ Client struct {
+ config config.Config
+ service Ydb_Scripting_V1.ScriptingServiceClient
+ }
+)
func (c *Client) Execute(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ parameters *params.Parameters,
) (r result.Result, err error) {
if c == nil {
return r, xerrors.WithStackTrace(errNilClient)
}
call := func(ctx context.Context) error {
- r, err = c.execute(ctx, query, params)
+ r, err = c.execute(ctx, query, parameters)
return xerrors.WithStackTrace(err)
}
@@ -66,17 +68,17 @@ func (c *Client) Execute(
func (c *Client) execute(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ parameters *params.Parameters,
) (r result.Result, err error) {
var (
onDone = trace.ScriptingOnExecute(c.config.Trace(), &ctx,
- stack.FunctionID(""),
- query, params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scripting.(*Client).execute"),
+ query, parameters,
)
a = allocator.New()
request = &Ydb_Scripting.ExecuteYqlRequest{
Script: query,
- Parameters: params.Params().ToYDB(a),
+ Parameters: parameters.ToYDB(a),
OperationParams: operation.Params(
ctx,
c.config.OperationTimeout(),
@@ -149,7 +151,7 @@ func (c *Client) explain(
) (e table.ScriptingYQLExplanation, err error) {
var (
onDone = trace.ScriptingOnExplain(c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scripting.(*Client).explain"),
query,
)
request = &Ydb_Scripting.ExplainYqlRequest{
@@ -184,7 +186,7 @@ func (c *Client) explain(
ParameterTypes: make(map[string]types.Type, len(result.GetParametersTypes())),
}
for k, v := range result.GetParametersTypes() {
- e.ParameterTypes[k] = value.TypeFromYDB(v)
+ e.ParameterTypes[k] = types.TypeFromYDB(v)
}
return e, nil
@@ -193,7 +195,7 @@ func (c *Client) explain(
func (c *Client) StreamExecute(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ params *params.Parameters,
) (r result.StreamResult, err error) {
if c == nil {
return r, xerrors.WithStackTrace(errNilClient)
@@ -219,17 +221,17 @@ func (c *Client) StreamExecute(
func (c *Client) streamExecute(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ parameters *params.Parameters,
) (r result.StreamResult, err error) {
var (
onIntermediate = trace.ScriptingOnStreamExecute(c.config.Trace(), &ctx,
- stack.FunctionID(""),
- query, params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scripting.(*Client).streamExecute"),
+ query, parameters,
)
a = allocator.New()
request = &Ydb_Scripting.ExecuteYqlRequest{
Script: query,
- Parameters: params.Params().ToYDB(a),
+ Parameters: parameters.ToYDB(a),
OperationParams: operation.Params(
ctx,
c.config.OperationTimeout(),
@@ -290,7 +292,9 @@ func (c *Client) Close(ctx context.Context) (err error) {
if c == nil {
return xerrors.WithStackTrace(errNilClient)
}
- onDone := trace.ScriptingOnClose(c.config.Trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.ScriptingOnClose(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/scripting.(*Client).Close"),
+ )
defer func() {
onDone(err)
}()
@@ -298,9 +302,9 @@ func (c *Client) Close(ctx context.Context) (err error) {
return nil
}
-func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) (*Client, error) {
+func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client {
return &Client{
config: config,
service: Ydb_Scripting_V1.NewScriptingServiceClient(cc),
- }, nil
+ }
}
diff --git a/internal/scripting/config/config.go b/internal/scripting/config/config.go
index 1b8ee0ff3..35f90651d 100644
--- a/internal/scripting/config/config.go
+++ b/internal/scripting/config/config.go
@@ -36,9 +36,9 @@ func New(opts ...Option) Config {
c := Config{
trace: &trace.Scripting{},
}
- for _, o := range opts {
- if o != nil {
- o(&c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&c)
}
}
diff --git a/internal/session/status.go b/internal/session/status.go
new file mode 100644
index 000000000..49fbe7425
--- /dev/null
+++ b/internal/session/status.go
@@ -0,0 +1,12 @@
+package session
+
+type Status = string
+
+const (
+ StatusUnknown = Status("Unknown")
+ StatusIdle = Status("Idle")
+ StatusInUse = Status("InUse")
+ StatusClosing = Status("Closing")
+ StatusClosed = Status("Closed")
+ StatusError = Status("Error")
+)
diff --git a/internal/stack/function_id.go b/internal/stack/function_id.go
index bbd0f17f3..646a7f5ea 100644
--- a/internal/stack/function_id.go
+++ b/internal/stack/function_id.go
@@ -1,10 +1,10 @@
package stack
-type caller interface {
+type Caller interface {
FunctionID() string
}
-var _ caller = functionID("")
+var _ Caller = functionID("")
type functionID string
@@ -12,7 +12,7 @@ func (id functionID) FunctionID() string {
return string(id)
}
-func FunctionID(id string) caller {
+func FunctionID(id string) Caller {
if id != "" {
return functionID(id)
}
diff --git a/internal/stack/function_id_test.go b/internal/stack/function_id_test.go
new file mode 100644
index 000000000..b7adabee5
--- /dev/null
+++ b/internal/stack/function_id_test.go
@@ -0,0 +1,69 @@
+package stack
+
+import (
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+type genericType[T any] struct{}
+
+type starType struct{}
+
+func (t genericType[T]) Call() string {
+ return FunctionID("").FunctionID()
+}
+
+func staticCall() string {
+ return FunctionID("").FunctionID()
+}
+
+func (e *starType) starredCall() string {
+ return FunctionID("").FunctionID()
+}
+
+func anonymousFunctionCall() string {
+ var result string
+ var mu sync.Mutex
+ go func() {
+ mu.Lock()
+ defer mu.Unlock()
+ result = FunctionID("").FunctionID()
+ }()
+ time.Sleep(time.Second)
+
+ mu.Lock()
+ defer mu.Unlock()
+
+ return result
+}
+
+func TestFunctionIDForGenericType(t *testing.T) {
+ t.Run("StaticFunc", func(t *testing.T) {
+ require.Equal(t,
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack.staticCall",
+ staticCall(),
+ )
+ })
+ t.Run("GenericTypeCall", func(t *testing.T) {
+ require.Equal(t,
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack.genericType.Call",
+ genericType[uint64]{}.Call(),
+ )
+ })
+ t.Run("StarTypeCall", func(t *testing.T) {
+ x := starType{}
+ require.Equal(t,
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack.(*starType).starredCall",
+ x.starredCall(),
+ )
+ })
+ t.Run("AnonymousFunctionCall", func(t *testing.T) {
+ require.Equal(t,
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack.anonymousFunctionCall",
+ anonymousFunctionCall(),
+ )
+ })
+}
diff --git a/internal/stack/record.go b/internal/stack/record.go
index 2fbf29ae5..2098c1ec8 100644
--- a/internal/stack/record.go
+++ b/internal/stack/record.go
@@ -62,7 +62,7 @@ func PackagePath(b bool) recordOption {
}
}
-var _ caller = call{}
+var _ Caller = call{}
type call struct {
function uintptr
@@ -87,7 +87,9 @@ func (c call) Record(opts ...recordOption) string {
lambdas: true,
}
for _, opt := range opts {
- opt(&optionsHolder)
+ if opt != nil {
+ opt(&optionsHolder)
+ }
}
name := runtime.FuncForPC(c.function).Name()
var (
@@ -103,6 +105,7 @@ func (c call) Record(opts ...recordOption) string {
if i := strings.LastIndex(name, "/"); i > -1 {
pkgPath, name = name[:i], name[i+1:]
}
+ name = strings.ReplaceAll(name, "[...]", "")
split := strings.Split(name, ".")
lambdas := make([]string, 0, len(split))
for i := range split {
diff --git a/internal/table/client.go b/internal/table/client.go
index 1ad82c518..7d063346b 100644
--- a/internal/table/client.go
+++ b/internal/table/client.go
@@ -34,7 +34,14 @@ type balancer interface {
nodeChecker
}
-func New(ctx context.Context, balancer balancer, config *config.Config) (*Client, error) {
+func New(ctx context.Context, balancer balancer, config *config.Config) *Client {
+ onDone := trace.TableOnInit(config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.New"),
+ )
+ defer func() {
+ onDone(config.SizeLimit())
+ }()
+
return newClient(ctx, balancer, func(ctx context.Context) (s *session, err error) {
return newSession(ctx, balancer, config)
}, config)
@@ -45,12 +52,8 @@ func newClient(
balancer balancer,
builder sessionBuilder,
config *config.Config,
-) (c *Client, finalErr error) {
- onDone := trace.TableOnInit(config.Trace(), &ctx, stack.FunctionID(""))
- defer func() {
- onDone(config.SizeLimit(), finalErr)
- }()
- c = &Client{
+) *Client {
+ c := &Client{
clock: config.Clock(),
config: config,
cc: balancer,
@@ -74,7 +77,7 @@ func newClient(
go c.internalPoolGC(ctx, idleThreshold)
}
- return c, nil
+ return c
}
// Client is a set of session instances that may be reused.
@@ -121,9 +124,9 @@ func withCreateSessionOnClose(onClose func(s *session)) createSessionOption {
func (c *Client) createSession(ctx context.Context, opts ...createSessionOption) (s *session, err error) {
options := createSessionOptions{}
- for _, o := range opts {
- if o != nil {
- o(&options)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&options)
}
}
@@ -165,7 +168,7 @@ func (c *Client) createSession(ctx context.Context, opts ...createSessionOption)
err error
)
- createSessionCtx := xcontext.WithoutDeadline(ctx)
+ createSessionCtx := xcontext.ValueOnly(ctx)
if timeout := c.config.CreateSessionTimeout(); timeout > 0 {
var cancel context.CancelFunc
@@ -178,7 +181,7 @@ func (c *Client) createSession(ctx context.Context, opts ...createSessionOption)
return
}
- closeSessionCtx := xcontext.WithoutDeadline(ctx)
+ closeSessionCtx := xcontext.ValueOnly(ctx)
if timeout := c.config.DeleteTimeout(); timeout > 0 {
var cancel context.CancelFunc
@@ -260,15 +263,13 @@ func (c *Client) CreateSession(ctx context.Context, opts ...table.Option) (_ tab
[]retry.Option{
retry.WithIdempotent(true),
retry.WithTrace(&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- onIntermediate := trace.TableOnCreateSession(c.config.Trace(), info.Context, stack.FunctionID(""))
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ onDone := trace.TableOnCreateSession(c.config.Trace(), info.Context,
+ stack.FunctionID(
+ "github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).CreateSession"))
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- onDone := onIntermediate(info.Error)
-
- return func(info trace.RetryLoopDoneInfo) {
- onDone(s, info.Attempts, info.Error)
- }
+ return func(info trace.RetryLoopDoneInfo) {
+ onDone(s, info.Attempts, info.Error)
}
},
}),
@@ -380,7 +381,9 @@ func (c *Client) internalPoolGet(ctx context.Context, opts ...getOption) (s *ses
}
}
- onDone := trace.TableOnPoolGet(o.t, &ctx, stack.FunctionID(""))
+ onDone := trace.TableOnPoolGet(o.t, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).internalPoolGet"),
+ )
defer func() {
onDone(s, i, err)
}()
@@ -476,7 +479,9 @@ func (c *Client) internalPoolWaitFromCh(ctx context.Context, t *trace.Table) (s
el = c.waitQ.PushBack(ch)
})
- waitDone := trace.TableOnPoolWait(t, &ctx, stack.FunctionID(""))
+ waitDone := trace.TableOnPoolWait(t, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).internalPoolWaitFromCh"),
+ )
defer func() {
waitDone(s, err)
@@ -484,7 +489,10 @@ func (c *Client) internalPoolWaitFromCh(ctx context.Context, t *trace.Table) (s
var createSessionTimeoutCh <-chan time.Time
if timeout := c.config.CreateSessionTimeout(); timeout > 0 {
- createSessionTimeoutCh = c.clock.After(timeout)
+ createSessionTimeoutChTimer := c.clock.NewTimer(timeout)
+ defer createSessionTimeoutChTimer.Stop()
+
+ createSessionTimeoutCh = createSessionTimeoutChTimer.Chan()
}
select {
@@ -538,7 +546,7 @@ func (c *Client) internalPoolWaitFromCh(ctx context.Context, t *trace.Table) (s
// panic.
func (c *Client) Put(ctx context.Context, s *session) (err error) {
onDone := trace.TableOnPoolPut(c.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).Put"),
s,
)
defer func() {
@@ -597,7 +605,9 @@ func (c *Client) Close(ctx context.Context) (err error) {
default:
close(c.done)
- onDone := trace.TableOnClose(c.config.Trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.TableOnClose(c.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).Close"),
+ )
defer func() {
onDone(err)
}()
@@ -642,17 +652,16 @@ func (c *Client) Do(ctx context.Context, op table.Operation, opts ...table.Optio
config := c.retryOptions(opts...)
- attempts, onIntermediate := 0, trace.TableOnDo(config.Trace, &ctx,
- stack.FunctionID(""),
- config.Label, config.Label, config.Idempotent, xcontext.IsNestedCall(ctx),
+ attempts, onDone := 0, trace.TableOnDo(config.Trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).Do"),
+ config.Label, config.Idempotent, xcontext.IsNestedCall(ctx),
)
defer func() {
- onIntermediate(finalErr)(attempts, finalErr)
+ onDone(attempts, finalErr)
}()
err := do(ctx, c, c.config, op, func(err error) {
attempts++
- onIntermediate(err)
}, config.RetryOptions...)
if err != nil {
return xerrors.WithStackTrace(err)
@@ -672,22 +681,18 @@ func (c *Client) DoTx(ctx context.Context, op table.TxOperation, opts ...table.O
config := c.retryOptions(opts...)
- attempts, onIntermediate := 0, trace.TableOnDoTx(config.Trace, &ctx,
- stack.FunctionID(""),
- config.Label, config.Label, config.Idempotent, xcontext.IsNestedCall(ctx),
+ attempts, onDone := 0, trace.TableOnDoTx(config.Trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*Client).DoTx"),
+ config.Label, config.Idempotent, xcontext.IsNestedCall(ctx),
)
defer func() {
- onIntermediate(finalErr)(attempts, finalErr)
+ onDone(attempts, finalErr)
}()
return retryBackoff(ctx, c,
func(ctx context.Context, s table.Session) (err error) {
attempts++
- defer func() {
- onIntermediate(err)
- }()
-
tx, err := s.BeginTransaction(ctx, config.TxSettings)
if err != nil {
return xerrors.WithStackTrace(err)
diff --git a/internal/table/client_test.go b/internal/table/client_test.go
index f1f1cc2f9..82b7eb6af 100644
--- a/internal/table/client_test.go
+++ b/internal/table/client_test.go
@@ -408,7 +408,7 @@ func TestSessionPoolRacyGet(t *testing.T) {
session *session
}
create := make(chan createReq)
- p, err := newClient(
+ p := newClient(
context.Background(),
nil,
(&StubBuilder{
@@ -429,10 +429,10 @@ func TestSessionPoolRacyGet(t *testing.T) {
config.WithIdleThreshold(-1),
),
)
- require.NoError(t, err)
var (
expSession *session
done = make(chan struct{}, 2)
+ err error
)
for i := 0; i < 2; i++ {
go func() {
@@ -872,7 +872,7 @@ func newClientWithStubBuilder(
stubLimit int,
options ...config.Option,
) *Client {
- c, err := newClient(
+ c := newClient(
context.Background(),
balancer,
(&StubBuilder{
@@ -882,7 +882,6 @@ func newClientWithStubBuilder(
}).createSession,
config.New(options...),
)
- require.NoError(t, err)
return c
}
diff --git a/internal/table/config/config.go b/internal/table/config/config.go
index 5013ab332..de94fb3e6 100644
--- a/internal/table/config/config.go
+++ b/internal/table/config/config.go
@@ -27,9 +27,9 @@ const (
func New(opts ...Option) *Config {
c := defaults()
- for _, o := range opts {
- if o != nil {
- o(c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
}
}
diff --git a/internal/table/params.go b/internal/table/params.go
deleted file mode 100644
index bb1f1e327..000000000
--- a/internal/table/params.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package table
-
-import (
- "bytes"
- "fmt"
- "sort"
-
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
- "github.com/ydb-platform/ydb-go-sdk/v3/table"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
-)
-
-var ErrNameRequired = xerrors.Wrap(fmt.Errorf("only named parameters are supported"))
-
-// GenerateDeclareSection generates DECLARE section text in YQL query by params
-//
-// Warning: This is an experimental feature and could change at any time
-func GenerateDeclareSection(params *table.QueryParameters) (string, error) {
- var (
- buf bytes.Buffer
- names []string
- declares = make(map[string]string, len(params.Params()))
- )
- params.Each(func(name string, v types.Value) {
- names = append(names, name)
- declares[name] = fmt.Sprintf(
- "DECLARE %s AS %s;\n",
- name,
- v.Type().Yql(),
- )
- })
- sort.Strings(names)
- for _, name := range names {
- if name == "" {
- return "", xerrors.WithStackTrace(ErrNameRequired)
- }
- buf.WriteString(declares[name])
- }
-
- return buf.String(), nil
-}
diff --git a/internal/table/scanner/result.go b/internal/table/scanner/result.go
index 1d931489c..fdebc2859 100644
--- a/internal/table/scanner/result.go
+++ b/internal/table/scanner/result.go
@@ -18,7 +18,7 @@ import (
var errAlreadyClosed = xerrors.Wrap(errors.New("result closed early"))
type baseResult struct {
- scanner
+ valueScanner
nextResultSetCounter atomic.Uint64
statsMtx xsync.RWMutex
@@ -36,7 +36,7 @@ type streamResult struct {
// Err returns error caused Scanner to be broken.
func (r *streamResult) Err() error {
- err := r.scanner.Err()
+ err := r.valueScanner.Err()
if err != nil {
return xerrors.WithStackTrace(err)
}
@@ -53,7 +53,7 @@ type unaryResult struct {
// Err returns error caused Scanner to be broken.
func (r *unaryResult) Err() error {
- err := r.scanner.Err()
+ err := r.valueScanner.Err()
if err != nil {
return xerrors.WithStackTrace(err)
}
@@ -96,13 +96,13 @@ type option func(r *baseResult)
func WithIgnoreTruncated(ignoreTruncated bool) option {
return func(r *baseResult) {
- r.scanner.ignoreTruncated = ignoreTruncated
+ r.valueScanner.ignoreTruncated = ignoreTruncated
}
}
func WithMarkTruncatedAsRetryable() option {
return func(r *baseResult) {
- r.scanner.markTruncatedAsRetryable = true
+ r.valueScanner.markTruncatedAsRetryable = true
}
}
@@ -116,9 +116,9 @@ func NewStream(
recv: recv,
close: onClose,
}
- for _, o := range opts {
- if o != nil {
- o(&r.baseResult)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&r.baseResult)
}
}
if err := r.nextResultSetErr(ctx); err != nil {
@@ -135,9 +135,9 @@ func NewUnary(sets []*Ydb.ResultSet, stats *Ydb_TableStats.QueryStats, opts ...o
},
sets: sets,
}
- for _, o := range opts {
- if o != nil {
- o(&r.baseResult)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&r.baseResult)
}
}
diff --git a/internal/table/scanner/result_test.go b/internal/table/scanner/result_test.go
index e3f71b62e..dc81b0203 100644
--- a/internal/table/scanner/result_test.go
+++ b/internal/table/scanner/result_test.go
@@ -13,29 +13,29 @@ import (
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
func TestResultAny(t *testing.T) {
for _, test := range []struct {
name string
columns []options.Column
- values []types.Value
+ values []value.Value
exp []interface{}
}{
{
columns: []options.Column{
{
Name: "column0",
- Type: types.Optional(types.TypeUint32),
+ Type: types.NewOptional(types.Uint32),
Family: "family0",
},
},
- values: []types.Value{
- types.OptionalValue(types.Uint32Value(43)),
- types.NullValue(types.TypeUint32),
+ values: []value.Value{
+ value.OptionalValue(value.Uint32Value(43)),
+ value.NullValue(types.Uint32),
},
exp: []interface{}{
uint32(43),
@@ -83,25 +83,25 @@ func TestResultOUint32(t *testing.T) {
for _, test := range []struct {
name string
columns []options.Column
- values []types.Value
+ values []value.Value
exp []uint32
}{
{
columns: []options.Column{
{
Name: "column0",
- Type: types.Optional(types.TypeUint32),
+ Type: types.NewOptional(types.Uint32),
Family: "family0",
},
{
Name: "column1",
- Type: types.TypeUint32,
+ Type: types.Uint32,
Family: "family0",
},
},
- values: []types.Value{
- types.OptionalValue(types.Uint32Value(43)),
- types.Uint32Value(43),
+ values: []value.Value{
+ value.OptionalValue(value.Uint32Value(43)),
+ value.Uint32Value(43),
},
exp: []uint32{
43,
@@ -151,13 +151,13 @@ func WithColumns(cs ...options.Column) ResultSetOption {
for _, c := range cs {
r.Columns = append(r.Columns, &Ydb.Column{
Name: c.Name,
- Type: value.TypeToYDB(c.Type, a),
+ Type: types.TypeToYDB(c.Type, a),
})
}
}
}
-func WithValues(vs ...types.Value) ResultSetOption {
+func WithValues(vs ...value.Value) ResultSetOption {
return func(r *resultSetDesc, a *allocator.Allocator) {
n := len(r.Columns)
if n == 0 {
@@ -178,15 +178,15 @@ func WithValues(vs ...types.Value) ResultSetOption {
}
}
tv := value.ToYDB(v, a)
- act := value.TypeFromYDB(tv.Type)
- exp := value.TypeFromYDB(r.Columns[j].Type)
- if !value.TypesEqual(act, exp) {
+ act := types.TypeFromYDB(tv.GetType())
+ exp := types.TypeFromYDB(r.Columns[j].GetType())
+ if !types.Equal(act, exp) {
panic(fmt.Sprintf(
"unexpected types for #%d column: %s; want %s",
j, act, exp,
))
}
- row.Items[j] = tv.Value
+ row.Items[j] = tv.GetValue()
}
if row != nil {
r.Rows = append(r.Rows, row)
diff --git a/internal/table/scanner/scan_raw.go b/internal/table/scanner/scan_raw.go
index 1a0c8be98..7c0782f3c 100644
--- a/internal/table/scanner/scan_raw.go
+++ b/internal/table/scanner/scan_raw.go
@@ -10,14 +10,15 @@ import (
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/decimal"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
type rawConverter struct {
- *scanner
+ *valueScanner
}
func (s *rawConverter) String() (v []byte) {
@@ -31,7 +32,7 @@ func (s *rawConverter) HasItems() bool {
}
func (s *rawConverter) HasNextItem() bool {
- return s.hasItems() && s.nextItem < len(s.row.Items)
+ return s.hasItems() && s.nextItem < len(s.row.GetItems())
}
func (s *rawConverter) Path() string {
@@ -282,8 +283,8 @@ func (s *rawConverter) Any() interface{} {
return s.any()
}
-// Value returns current item under scan as ydb.Value types.
-func (s *rawConverter) Value() types.Value {
+// Value returns current item under scan as value
+func (s *rawConverter) Value() value.Value {
if s.Err() != nil {
return nil
}
@@ -338,14 +339,14 @@ func (s *rawConverter) ListItem(i int) {
return
}
p := s.stack.parent()
- if !s.itemsBoundsCheck(p.v.Items, i) {
+ if !s.itemsBoundsCheck(p.v.GetItems(), i) {
return
}
if t := s.assertTypeList(p.t); t != nil {
s.stack.set(item{
i: i,
- t: t.ListType.Item,
- v: p.v.Items[i],
+ t: t.ListType.GetItem(),
+ v: p.v.GetItems()[i],
})
}
}
@@ -377,14 +378,14 @@ func (s *rawConverter) TupleItem(i int) {
return
}
p := s.stack.parent()
- if !s.itemsBoundsCheck(p.v.Items, i) {
+ if !s.itemsBoundsCheck(p.v.GetItems(), i) {
return
}
if t := s.assertTypeTuple(p.t); t != nil {
s.stack.set(item{
i: i,
- t: t.TupleType.Elements[i],
- v: p.v.Items[i],
+ t: t.TupleType.GetElements()[i],
+ v: p.v.GetItems()[i],
})
}
}
@@ -416,17 +417,17 @@ func (s *rawConverter) StructField(i int) (name string) {
return
}
p := s.stack.parent()
- if !s.itemsBoundsCheck(p.v.Items, i) {
+ if !s.itemsBoundsCheck(p.v.GetItems(), i) {
return
}
if t := s.assertTypeStruct(p.t); t != nil {
- m := t.StructType.Members[i]
- name = m.Name
+ m := t.StructType.GetMembers()[i]
+ name = m.GetName()
s.stack.set(item{
- name: m.Name,
+ name: m.GetName(),
i: i,
- t: m.Type,
- v: p.v.Items[i],
+ t: m.GetType(),
+ v: p.v.GetItems()[i],
})
}
@@ -460,14 +461,14 @@ func (s *rawConverter) DictKey(i int) {
return
}
p := s.stack.parent()
- if !s.pairsBoundsCheck(p.v.Pairs, i) {
+ if !s.pairsBoundsCheck(p.v.GetPairs(), i) {
return
}
if t := s.assertTypeDict(p.t); t != nil {
s.stack.set(item{
i: i,
- t: t.DictType.Key,
- v: p.v.Pairs[i].Key,
+ t: t.DictType.GetKey(),
+ v: p.v.GetPairs()[i].GetKey(),
})
}
}
@@ -477,14 +478,14 @@ func (s *rawConverter) DictPayload(i int) {
return
}
p := s.stack.parent()
- if !s.pairsBoundsCheck(p.v.Pairs, i) {
+ if !s.pairsBoundsCheck(p.v.GetPairs(), i) {
return
}
if t := s.assertTypeDict(p.t); t != nil {
s.stack.set(item{
i: i,
- t: t.DictType.Payload,
- v: p.v.Pairs[i].Payload,
+ t: t.DictType.GetPayload(),
+ v: p.v.GetPairs()[i].GetPayload(),
})
}
}
@@ -534,13 +535,13 @@ func (s *rawConverter) Unwrap() {
return
}
v := x.v
- if isOptional(t.OptionalType.Item) {
+ if isOptional(t.OptionalType.GetItem()) {
v = s.unwrapValue()
}
s.stack.enter()
s.stack.set(item{
name: "*",
- t: t.OptionalType.Item,
+ t: t.OptionalType.GetItem(),
v: v,
})
}
@@ -557,20 +558,20 @@ func (s *rawConverter) Decimal(t types.Type) (v [16]byte) {
return s.uint128()
}
-func (s *rawConverter) UnwrapDecimal() (v types.Decimal) {
+func (s *rawConverter) UnwrapDecimal() decimal.Decimal {
if s.Err() != nil {
- return
+ return decimal.Decimal{}
}
s.unwrap()
d := s.assertTypeDecimal(s.stack.current().t)
if d == nil {
- return
+ return decimal.Decimal{}
}
- return types.Decimal{
+ return decimal.Decimal{
Bytes: s.uint128(),
- Precision: d.DecimalType.Precision,
- Scale: d.DecimalType.Scale,
+ Precision: d.DecimalType.GetPrecision(),
+ Scale: d.DecimalType.GetScale(),
}
}
@@ -583,39 +584,39 @@ func (s *rawConverter) IsDecimal() bool {
}
func isEqualDecimal(d *Ydb.DecimalType, t types.Type) bool {
- w := t.(*value.DecimalType)
+ w := t.(*types.Decimal)
- return d.Precision == w.Precision && d.Scale == w.Scale
+ return d.GetPrecision() == w.Precision() && d.GetScale() == w.Scale()
}
func (s *rawConverter) isCurrentTypeDecimal() bool {
c := s.stack.current()
- _, ok := c.t.Type.(*Ydb.Type_DecimalType)
+ _, ok := c.t.GetType().(*Ydb.Type_DecimalType)
return ok
}
func (s *rawConverter) unwrapVariantType(typ *Ydb.Type_VariantType, index uint32) (name string, t *Ydb.Type) {
i := int(index)
- switch x := typ.VariantType.Type.(type) {
+ switch x := typ.VariantType.GetType().(type) {
case *Ydb.VariantType_TupleItems:
- if i >= len(x.TupleItems.Elements) {
+ if i >= len(x.TupleItems.GetElements()) {
_ = s.errorf(0, "unimplemented")
return
}
- return "", x.TupleItems.Elements[i]
+ return "", x.TupleItems.GetElements()[i]
case *Ydb.VariantType_StructItems:
- if i >= len(x.StructItems.Members) {
+ if i >= len(x.StructItems.GetMembers()) {
_ = s.errorf(0, "unimplemented")
return
}
- m := x.StructItems.Members[i]
+ m := x.StructItems.GetMembers()[i]
- return m.Name, m.Type
+ return m.GetName(), m.GetType()
default:
panic("unexpected variant items types")
@@ -628,7 +629,7 @@ func (s *rawConverter) variant() (v *Ydb.Value, index uint32) {
return
}
x := s.stack.current() // Is not nil if unwrapValue succeeded.
- index = x.v.VariantIndex
+ index = x.v.GetVariantIndex()
return
}
@@ -640,7 +641,7 @@ func (s *rawConverter) itemsIn() int {
}
s.stack.enter()
- return len(x.v.Items)
+ return len(x.v.GetItems())
}
func (s *rawConverter) itemsOut() {
@@ -658,7 +659,7 @@ func (s *rawConverter) pairsIn() int {
}
s.stack.enter()
- return len(x.v.Pairs)
+ return len(x.v.GetPairs())
}
func (s *rawConverter) pairsOut() {
@@ -679,8 +680,8 @@ func (s *rawConverter) boundsCheck(n, i int) bool {
return true
}
-func (s *scanner) assertTypeOptional(typ *Ydb.Type) (t *Ydb.Type_OptionalType) {
- x := typ.Type
+func (s *valueScanner) assertTypeOptional(typ *Ydb.Type) (t *Ydb.Type_OptionalType) {
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_OptionalType); t == nil {
s.typeError(x, t)
}
@@ -712,8 +713,8 @@ func (s *rawConverter) assertCurrentTypeNullable() bool {
func (s *rawConverter) assertCurrentTypeIs(t types.Type) bool {
c := s.stack.current()
- act := value.TypeFromYDB(c.t)
- if !value.TypesEqual(act, t) {
+ act := types.TypeFromYDB(c.t)
+ if !types.Equal(act, t) {
_ = s.errorf(
1,
"unexpected types at %q %s: %s; want %s",
@@ -744,7 +745,7 @@ func (s *rawConverter) assertCurrentTypeDecimal(t types.Type) bool {
}
func (s *rawConverter) assertTypeList(typ *Ydb.Type) (t *Ydb.Type_ListType) {
- x := typ.Type
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_ListType); t == nil {
s.typeError(x, t)
}
@@ -753,7 +754,7 @@ func (s *rawConverter) assertTypeList(typ *Ydb.Type) (t *Ydb.Type_ListType) {
}
func (s *rawConverter) assertTypeTuple(typ *Ydb.Type) (t *Ydb.Type_TupleType) {
- x := typ.Type
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_TupleType); t == nil {
s.typeError(x, t)
}
@@ -762,7 +763,7 @@ func (s *rawConverter) assertTypeTuple(typ *Ydb.Type) (t *Ydb.Type_TupleType) {
}
func (s *rawConverter) assertTypeStruct(typ *Ydb.Type) (t *Ydb.Type_StructType) {
- x := typ.Type
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_StructType); t == nil {
s.typeError(x, t)
}
@@ -771,7 +772,7 @@ func (s *rawConverter) assertTypeStruct(typ *Ydb.Type) (t *Ydb.Type_StructType)
}
func (s *rawConverter) assertTypeDict(typ *Ydb.Type) (t *Ydb.Type_DictType) {
- x := typ.Type
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_DictType); t == nil {
s.typeError(x, t)
}
@@ -780,7 +781,7 @@ func (s *rawConverter) assertTypeDict(typ *Ydb.Type) (t *Ydb.Type_DictType) {
}
func (s *rawConverter) assertTypeDecimal(typ *Ydb.Type) (t *Ydb.Type_DecimalType) {
- x := typ.Type
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_DecimalType); t == nil {
s.typeError(x, t)
}
@@ -789,7 +790,7 @@ func (s *rawConverter) assertTypeDecimal(typ *Ydb.Type) (t *Ydb.Type_DecimalType
}
func (s *rawConverter) assertTypeVariant(typ *Ydb.Type) (t *Ydb.Type_VariantType) {
- x := typ.Type
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_VariantType); t == nil {
s.typeError(x, t)
}
@@ -818,7 +819,7 @@ func nameIface(v interface{}) string {
t := reflect.TypeOf(v)
s := t.String()
s = strings.TrimPrefix(s, "*Ydb.Value_")
- s = strings.TrimSuffix(s, "Value")
+ s = strings.TrimSuffix(s, "valueType")
s = strings.TrimPrefix(s, "*Ydb.Type_")
s = strings.TrimSuffix(s, "Type")
diff --git a/internal/table/scanner/scanner.go b/internal/table/scanner/scanner.go
index 5cda9e3fd..8a3effb2b 100644
--- a/internal/table/scanner/scanner.go
+++ b/internal/table/scanner/scanner.go
@@ -11,6 +11,9 @@ import (
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/decimal"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/scanner"
+ internalTypes "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
@@ -19,10 +22,9 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/named"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
-type scanner struct {
+type valueScanner struct {
set *Ydb.ResultSet
row *Ydb.Value
converter *rawConverter
@@ -39,59 +41,59 @@ type scanner struct {
}
// ColumnCount returns number of columns in the current result set.
-func (s *scanner) ColumnCount() int {
+func (s *valueScanner) ColumnCount() int {
if s.set == nil {
return 0
}
- return len(s.set.Columns)
+ return len(s.set.GetColumns())
}
// Columns allows to iterate over all columns of the current result set.
-func (s *scanner) Columns(it func(options.Column)) {
+func (s *valueScanner) Columns(it func(options.Column)) {
if s.set == nil {
return
}
- for _, m := range s.set.Columns {
+ for _, m := range s.set.GetColumns() {
it(options.Column{
- Name: m.Name,
- Type: value.TypeFromYDB(m.Type),
+ Name: m.GetName(),
+ Type: internalTypes.TypeFromYDB(m.GetType()),
})
}
}
// RowCount returns number of rows in the result set.
-func (s *scanner) RowCount() int {
+func (s *valueScanner) RowCount() int {
if s.set == nil {
return 0
}
- return len(s.set.Rows)
+ return len(s.set.GetRows())
}
// ItemCount returns number of items in the current row.
-func (s *scanner) ItemCount() int {
+func (s *valueScanner) ItemCount() int {
if s.row == nil {
return 0
}
- return len(s.row.Items)
+ return len(s.row.GetItems())
}
// HasNextRow reports whether result row may be advanced.
// It may be useful to call HasNextRow() instead of NextRow() to look ahead
// without advancing the result rows.
-func (s *scanner) HasNextRow() bool {
- return s.err == nil && s.set != nil && s.nextRow < len(s.set.Rows)
+func (s *valueScanner) HasNextRow() bool {
+ return s.err == nil && s.set != nil && s.nextRow < len(s.set.GetRows())
}
// NextRow selects next row in the current result set.
// It returns false if there are no more rows in the result set.
-func (s *scanner) NextRow() bool {
+func (s *valueScanner) NextRow() bool {
if !s.HasNextRow() {
return false
}
- s.row = s.set.Rows[s.nextRow]
+ s.row = s.set.GetRows()[s.nextRow]
s.nextRow++
s.nextItem = 0
s.stack.reset()
@@ -99,7 +101,7 @@ func (s *scanner) NextRow() bool {
return true
}
-func (s *scanner) preScanChecks(lenValues int) (err error) {
+func (s *valueScanner) preScanChecks(lenValues int) (err error) {
if s.columnIndexes != nil {
if len(s.columnIndexes) != lenValues {
return s.errorf(
@@ -120,7 +122,7 @@ func (s *scanner) preScanChecks(lenValues int) (err error) {
return s.Err()
}
-func (s *scanner) ScanWithDefaults(values ...indexed.Required) (err error) {
+func (s *valueScanner) ScanWithDefaults(values ...indexed.Required) (err error) {
if err = s.preScanChecks(len(values)); err != nil {
return
}
@@ -148,7 +150,7 @@ func (s *scanner) ScanWithDefaults(values ...indexed.Required) (err error) {
return s.Err()
}
-func (s *scanner) Scan(values ...indexed.RequiredOrOptional) (err error) {
+func (s *valueScanner) Scan(values ...indexed.RequiredOrOptional) (err error) {
if err = s.preScanChecks(len(values)); err != nil {
return
}
@@ -176,7 +178,7 @@ func (s *scanner) Scan(values ...indexed.RequiredOrOptional) (err error) {
return s.Err()
}
-func (s *scanner) ScanNamed(namedValues ...named.Value) error {
+func (s *valueScanner) ScanNamed(namedValues ...named.Value) error {
if err := s.Err(); err != nil {
return err
}
@@ -198,7 +200,7 @@ func (s *scanner) ScanNamed(namedValues ...named.Value) error {
case named.TypeOptionalWithUseDefault:
s.scanOptional(namedValues[i].Value, true)
default:
- panic(fmt.Sprintf("unknown type of named.Value: %d", t))
+ panic(fmt.Sprintf("unknown type of named.valueType: %d", t))
}
}
s.nextItem += len(namedValues)
@@ -207,27 +209,27 @@ func (s *scanner) ScanNamed(namedValues ...named.Value) error {
}
// Truncated returns true if current result set has been truncated by server
-func (s *scanner) Truncated() bool {
+func (s *valueScanner) Truncated() bool {
if s.set == nil {
_ = s.errorf(0, "there are no sets in the scanner")
return false
}
- return s.set.Truncated
+ return s.set.GetTruncated()
}
// Truncated returns true if current result set has been truncated by server
-func (s *scanner) truncated() bool {
+func (s *valueScanner) truncated() bool {
if s.set == nil {
return false
}
- return s.set.Truncated
+ return s.set.GetTruncated()
}
// Err returns error caused Scanner to be broken.
-func (s *scanner) Err() error {
+func (s *valueScanner) Err() error {
s.errMtx.RLock()
defer s.errMtx.RUnlock()
if s.err != nil {
@@ -248,7 +250,7 @@ func (s *scanner) Err() error {
}
// Must not be exported.
-func (s *scanner) reset(set *Ydb.ResultSet, columnNames ...string) {
+func (s *valueScanner) reset(set *Ydb.ResultSet, columnNames ...string) {
s.set = set
s.row = nil
s.nextRow = 0
@@ -257,11 +259,11 @@ func (s *scanner) reset(set *Ydb.ResultSet, columnNames ...string) {
s.setColumnIndexes(columnNames)
s.stack.reset()
s.converter = &rawConverter{
- scanner: s,
+ valueScanner: s,
}
}
-func (s *scanner) path() string {
+func (s *valueScanner) path() string {
buf := xstring.Buffer()
defer buf.Free()
_, _ = s.writePathTo(buf)
@@ -269,7 +271,7 @@ func (s *scanner) path() string {
return buf.String()
}
-func (s *scanner) writePathTo(w io.Writer) (n int64, err error) {
+func (s *valueScanner) writePathTo(w io.Writer) (n int64, err error) {
x := s.stack.current()
st := x.name
m, err := io.WriteString(w, st)
@@ -281,42 +283,42 @@ func (s *scanner) writePathTo(w io.Writer) (n int64, err error) {
return n, nil
}
-func (s *scanner) getType() types.Type {
+func (s *valueScanner) getType() internalTypes.Type {
x := s.stack.current()
if x.isEmpty() {
return nil
}
- return value.TypeFromYDB(x.t)
+ return internalTypes.TypeFromYDB(x.t)
}
-func (s *scanner) hasItems() bool {
+func (s *valueScanner) hasItems() bool {
return s.err == nil && s.set != nil && s.row != nil
}
-func (s *scanner) seekItemByID(id int) error {
- if !s.hasItems() || id >= len(s.set.Columns) {
+func (s *valueScanner) seekItemByID(id int) error {
+ if !s.hasItems() || id >= len(s.set.GetColumns()) {
return s.notFoundColumnByIndex(id)
}
- col := s.set.Columns[id]
- s.stack.scanItem.name = col.Name
- s.stack.scanItem.t = col.Type
- s.stack.scanItem.v = s.row.Items[id]
+ col := s.set.GetColumns()[id]
+ s.stack.scanItem.name = col.GetName()
+ s.stack.scanItem.t = col.GetType()
+ s.stack.scanItem.v = s.row.GetItems()[id]
return nil
}
-func (s *scanner) seekItemByName(name string) error {
+func (s *valueScanner) seekItemByName(name string) error {
if !s.hasItems() {
return s.notFoundColumnName(name)
}
- for i, c := range s.set.Columns {
- if name != c.Name {
+ for i, c := range s.set.GetColumns() {
+ if name != c.GetName() {
continue
}
- s.stack.scanItem.name = c.Name
- s.stack.scanItem.t = c.Type
- s.stack.scanItem.v = s.row.Items[i]
+ s.stack.scanItem.name = c.GetName()
+ s.stack.scanItem.t = c.GetType()
+ s.stack.scanItem.v = s.row.GetItems()[i]
return s.Err()
}
@@ -324,7 +326,7 @@ func (s *scanner) seekItemByName(name string) error {
return s.notFoundColumnName(name)
}
-func (s *scanner) setColumnIndexes(columns []string) {
+func (s *valueScanner) setColumnIndexes(columns []string) {
if columns == nil {
s.columnIndexes = nil
@@ -333,8 +335,8 @@ func (s *scanner) setColumnIndexes(columns []string) {
s.columnIndexes = make([]int, len(columns))
for i, col := range columns {
found := false
- for j, c := range s.set.Columns {
- if c.Name == col {
+ for j, c := range s.set.GetColumns() {
+ if c.GetName() == col {
s.columnIndexes[i] = j
found = true
@@ -368,7 +370,7 @@ func (s *scanner) setColumnIndexes(columns []string) {
// [16]byte
//
//nolint:gocyclo
-func (s *scanner) any() interface{} {
+func (s *valueScanner) any() interface{} {
x := s.stack.current()
if s.Err() != nil || x.isEmpty() {
return nil
@@ -383,74 +385,74 @@ func (s *scanner) any() interface{} {
x = s.stack.current()
}
- t := value.TypeFromYDB(x.t)
- p, primitive := t.(value.PrimitiveType)
+ t := internalTypes.TypeFromYDB(x.t)
+ p, primitive := t.(internalTypes.Primitive)
if !primitive {
return s.value()
}
switch p {
- case value.TypeBool:
+ case internalTypes.Bool:
return s.bool()
- case value.TypeInt8:
+ case internalTypes.Int8:
return s.int8()
- case value.TypeUint8:
+ case internalTypes.Uint8:
return s.uint8()
- case value.TypeInt16:
+ case internalTypes.Int16:
return s.int16()
- case value.TypeUint16:
+ case internalTypes.Uint16:
return s.uint16()
- case value.TypeInt32:
+ case internalTypes.Int32:
return s.int32()
- case value.TypeFloat:
+ case internalTypes.Float:
return s.float()
- case value.TypeDouble:
+ case internalTypes.Double:
return s.double()
- case value.TypeBytes:
+ case internalTypes.Bytes:
return s.bytes()
- case value.TypeUUID:
+ case internalTypes.UUID:
return s.uint128()
- case value.TypeUint32:
+ case internalTypes.Uint32:
return s.uint32()
- case value.TypeDate:
+ case internalTypes.Date:
return value.DateToTime(s.uint32())
- case value.TypeDatetime:
+ case internalTypes.Datetime:
return value.DatetimeToTime(s.uint32())
- case value.TypeUint64:
+ case internalTypes.Uint64:
return s.uint64()
- case value.TypeTimestamp:
+ case internalTypes.Timestamp:
return value.TimestampToTime(s.uint64())
- case value.TypeInt64:
+ case internalTypes.Int64:
return s.int64()
- case value.TypeInterval:
+ case internalTypes.Interval:
return value.IntervalToDuration(s.int64())
- case value.TypeTzDate:
+ case internalTypes.TzDate:
src, err := value.TzDateToTime(s.text())
if err != nil {
- _ = s.errorf(0, "scanner.any(): %w", err)
+ _ = s.errorf(0, "valueScanner.any(): %w", err)
}
return src
- case value.TypeTzDatetime:
+ case internalTypes.TzDatetime:
src, err := value.TzDatetimeToTime(s.text())
if err != nil {
- _ = s.errorf(0, "scanner.any(): %w", err)
+ _ = s.errorf(0, "valueScanner.any(): %w", err)
}
return src
- case value.TypeTzTimestamp:
+ case internalTypes.TzTimestamp:
src, err := value.TzTimestampToTime(s.text())
if err != nil {
- _ = s.errorf(0, "scanner.any(): %w", err)
+ _ = s.errorf(0, "valueScanner.any(): %w", err)
}
return src
- case value.TypeText, value.TypeDyNumber:
+ case internalTypes.Text, internalTypes.DyNumber:
return s.text()
case
- value.TypeYSON,
- value.TypeJSON,
- value.TypeJSONDocument:
+ internalTypes.YSON,
+ internalTypes.JSON,
+ internalTypes.JSONDocument:
return xstring.ToBytes(s.text())
default:
_ = s.errorf(0, "unknown primitive types")
@@ -459,28 +461,28 @@ func (s *scanner) any() interface{} {
}
}
-// Value returns current item under scan as ydb.Value types.
-func (s *scanner) value() types.Value {
+// valueType returns current item under scan as ydb.valueType types
+func (s *valueScanner) value() value.Value {
x := s.stack.current()
return value.FromYDB(x.t, x.v)
}
-func (s *scanner) isCurrentTypeOptional() bool {
+func (s *valueScanner) isCurrentTypeOptional() bool {
c := s.stack.current()
return isOptional(c.t)
}
-func (s *scanner) isNull() bool {
+func (s *valueScanner) isNull() bool {
_, yes := s.stack.currentValue().(*Ydb.Value_NullFlagValue)
return yes
}
-// unwrap current item under scan interpreting it as Optional types.
+// unwrap current item under scan interpreting it as Optional types
// ignores if type is not optional
-func (s *scanner) unwrap() {
+func (s *valueScanner) unwrap() {
if s.Err() != nil {
return
}
@@ -490,13 +492,13 @@ func (s *scanner) unwrap() {
return
}
- if isOptional(t.OptionalType.Item) {
+ if isOptional(t.OptionalType.GetItem()) {
s.stack.scanItem.v = s.unwrapValue()
}
- s.stack.scanItem.t = t.OptionalType.Item
+ s.stack.scanItem.t = t.OptionalType.GetItem()
}
-func (s *scanner) unwrapValue() (v *Ydb.Value) {
+func (s *valueScanner) unwrapValue() (v *Ydb.Value) {
x, _ := s.stack.currentValue().(*Ydb.Value_NestedValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -507,25 +509,25 @@ func (s *scanner) unwrapValue() (v *Ydb.Value) {
return x.NestedValue
}
-func (s *scanner) unwrapDecimal() (v types.Decimal) {
+func (s *valueScanner) unwrapDecimal() decimal.Decimal {
if s.Err() != nil {
- return
+ return decimal.Decimal{}
}
s.unwrap()
d := s.assertTypeDecimal(s.stack.current().t)
if d == nil {
- return
+ return decimal.Decimal{}
}
- return types.Decimal{
+ return decimal.Decimal{
Bytes: s.uint128(),
- Precision: d.DecimalType.Precision,
- Scale: d.DecimalType.Scale,
+ Precision: d.DecimalType.GetPrecision(),
+ Scale: d.DecimalType.GetScale(),
}
}
-func (s *scanner) assertTypeDecimal(typ *Ydb.Type) (t *Ydb.Type_DecimalType) {
- x := typ.Type
+func (s *valueScanner) assertTypeDecimal(typ *Ydb.Type) (t *Ydb.Type_DecimalType) {
+ x := typ.GetType()
if t, _ = x.(*Ydb.Type_DecimalType); t == nil {
s.typeError(x, t)
}
@@ -533,7 +535,7 @@ func (s *scanner) assertTypeDecimal(typ *Ydb.Type) (t *Ydb.Type_DecimalType) {
return
}
-func (s *scanner) bool() (v bool) {
+func (s *valueScanner) bool() (v bool) {
x, _ := s.stack.currentValue().(*Ydb.Value_BoolValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -544,7 +546,7 @@ func (s *scanner) bool() (v bool) {
return x.BoolValue
}
-func (s *scanner) int8() (v int8) {
+func (s *valueScanner) int8() (v int8) {
d := s.int32()
if d < math.MinInt8 || math.MaxInt8 < d {
_ = s.overflowError(d, v)
@@ -555,7 +557,7 @@ func (s *scanner) int8() (v int8) {
return int8(d)
}
-func (s *scanner) uint8() (v uint8) {
+func (s *valueScanner) uint8() (v uint8) {
d := s.uint32()
if d > math.MaxUint8 {
_ = s.overflowError(d, v)
@@ -566,7 +568,7 @@ func (s *scanner) uint8() (v uint8) {
return uint8(d)
}
-func (s *scanner) int16() (v int16) {
+func (s *valueScanner) int16() (v int16) {
d := s.int32()
if d < math.MinInt16 || math.MaxInt16 < d {
_ = s.overflowError(d, v)
@@ -577,7 +579,7 @@ func (s *scanner) int16() (v int16) {
return int16(d)
}
-func (s *scanner) uint16() (v uint16) {
+func (s *valueScanner) uint16() (v uint16) {
d := s.uint32()
if d > math.MaxUint16 {
_ = s.overflowError(d, v)
@@ -588,7 +590,7 @@ func (s *scanner) uint16() (v uint16) {
return uint16(d)
}
-func (s *scanner) int32() (v int32) {
+func (s *valueScanner) int32() (v int32) {
x, _ := s.stack.currentValue().(*Ydb.Value_Int32Value)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -599,7 +601,7 @@ func (s *scanner) int32() (v int32) {
return x.Int32Value
}
-func (s *scanner) uint32() (v uint32) {
+func (s *valueScanner) uint32() (v uint32) {
x, _ := s.stack.currentValue().(*Ydb.Value_Uint32Value)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -610,7 +612,7 @@ func (s *scanner) uint32() (v uint32) {
return x.Uint32Value
}
-func (s *scanner) int64() (v int64) {
+func (s *valueScanner) int64() (v int64) {
x, _ := s.stack.currentValue().(*Ydb.Value_Int64Value)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -621,7 +623,7 @@ func (s *scanner) int64() (v int64) {
return x.Int64Value
}
-func (s *scanner) uint64() (v uint64) {
+func (s *valueScanner) uint64() (v uint64) {
x, _ := s.stack.currentValue().(*Ydb.Value_Uint64Value)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -632,7 +634,7 @@ func (s *scanner) uint64() (v uint64) {
return x.Uint64Value
}
-func (s *scanner) float() (v float32) {
+func (s *valueScanner) float() (v float32) {
x, _ := s.stack.currentValue().(*Ydb.Value_FloatValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -643,7 +645,7 @@ func (s *scanner) float() (v float32) {
return x.FloatValue
}
-func (s *scanner) double() (v float64) {
+func (s *valueScanner) double() (v float64) {
x, _ := s.stack.currentValue().(*Ydb.Value_DoubleValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -654,7 +656,7 @@ func (s *scanner) double() (v float64) {
return x.DoubleValue
}
-func (s *scanner) bytes() (v []byte) {
+func (s *valueScanner) bytes() (v []byte) {
x, _ := s.stack.currentValue().(*Ydb.Value_BytesValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -665,7 +667,7 @@ func (s *scanner) bytes() (v []byte) {
return x.BytesValue
}
-func (s *scanner) text() (v string) {
+func (s *valueScanner) text() (v string) {
x, _ := s.stack.currentValue().(*Ydb.Value_TextValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -676,7 +678,7 @@ func (s *scanner) text() (v string) {
return x.TextValue
}
-func (s *scanner) low128() (v uint64) {
+func (s *valueScanner) low128() (v uint64) {
x, _ := s.stack.currentValue().(*Ydb.Value_Low_128)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
@@ -687,7 +689,7 @@ func (s *scanner) low128() (v uint64) {
return x.Low_128
}
-func (s *scanner) uint128() (v [16]byte) {
+func (s *valueScanner) uint128() (v [16]byte) {
c := s.stack.current()
if c.isEmpty() {
_ = s.errorf(0, "not implemented convert to [16]byte")
@@ -695,19 +697,19 @@ func (s *scanner) uint128() (v [16]byte) {
return
}
lo := s.low128()
- hi := c.v.High_128
+ hi := c.v.GetHigh_128()
return value.BigEndianUint128(hi, lo)
}
-func (s *scanner) null() {
+func (s *valueScanner) null() {
x, _ := s.stack.currentValue().(*Ydb.Value_NullFlagValue)
if x == nil {
s.valueTypeError(s.stack.currentValue(), x)
}
}
-func (s *scanner) setTime(dst *time.Time) {
+func (s *valueScanner) setTime(dst *time.Time) {
switch t := s.stack.current().t.GetTypeId(); t {
case Ydb.Type_DATE:
*dst = value.DateToTime(s.uint32())
@@ -718,27 +720,27 @@ func (s *scanner) setTime(dst *time.Time) {
case Ydb.Type_TZ_DATE:
src, err := value.TzDateToTime(s.text())
if err != nil {
- _ = s.errorf(0, "scanner.setTime(): %w", err)
+ _ = s.errorf(0, "valueScanner.setTime(): %w", err)
}
*dst = src
case Ydb.Type_TZ_DATETIME:
src, err := value.TzDatetimeToTime(s.text())
if err != nil {
- _ = s.errorf(0, "scanner.setTime(): %w", err)
+ _ = s.errorf(0, "valueScanner.setTime(): %w", err)
}
*dst = src
case Ydb.Type_TZ_TIMESTAMP:
src, err := value.TzTimestampToTime(s.text())
if err != nil {
- _ = s.errorf(0, "scanner.setTime(): %w", err)
+ _ = s.errorf(0, "valueScanner.setTime(): %w", err)
}
*dst = src
default:
- _ = s.errorf(0, "scanner.setTime(): incorrect source types %s", t)
+ _ = s.errorf(0, "valueScanner.setTime(): incorrect source types %s", t)
}
}
-func (s *scanner) setString(dst *string) {
+func (s *valueScanner) setString(dst *string) {
switch t := s.stack.current().t.GetTypeId(); t {
case Ydb.Type_UUID:
src := s.uint128()
@@ -752,7 +754,7 @@ func (s *scanner) setString(dst *string) {
}
}
-func (s *scanner) setByte(dst *[]byte) {
+func (s *valueScanner) setByte(dst *[]byte) {
switch t := s.stack.current().t.GetTypeId(); t {
case Ydb.Type_UUID:
src := s.uint128()
@@ -766,7 +768,7 @@ func (s *scanner) setByte(dst *[]byte) {
}
}
-func (s *scanner) trySetByteArray(v interface{}, optional, def bool) bool {
+func (s *valueScanner) trySetByteArray(v interface{}, optional, def bool) bool {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
@@ -808,7 +810,7 @@ func (s *scanner) trySetByteArray(v interface{}, optional, def bool) bool {
}
//nolint:gocyclo
-func (s *scanner) scanRequired(v interface{}) {
+func (s *valueScanner) scanRequired(v interface{}) {
switch v := v.(type) {
case *bool:
*v = s.bool()
@@ -848,11 +850,11 @@ func (s *scanner) scanRequired(v interface{}) {
*v = s.uint128()
case *interface{}:
*v = s.any()
- case *types.Value:
+ case *value.Value:
*v = s.value()
- case *types.Decimal:
+ case *decimal.Decimal:
*v = s.unwrapDecimal()
- case types.Scanner:
+ case scanner.Scanner:
err := v.UnmarshalYDB(s.converter)
if err != nil {
_ = s.errorf(0, "ydb.Scanner error: %w", err)
@@ -865,9 +867,9 @@ func (s *scanner) scanRequired(v interface{}) {
case json.Unmarshaler:
var err error
switch s.getType() {
- case types.TypeJSON:
+ case internalTypes.JSON:
err = v.UnmarshalJSON(s.converter.JSON())
- case types.TypeJSONDocument:
+ case internalTypes.JSONDocument:
err = v.UnmarshalJSON(s.converter.JSONDocument())
default:
_ = s.errorf(0, "ydb required type %T not unsupported for applying to json.Unmarshaler", s.getType())
@@ -884,7 +886,7 @@ func (s *scanner) scanRequired(v interface{}) {
}
//nolint:gocyclo
-func (s *scanner) scanOptional(v interface{}, defaultValueForOptional bool) {
+func (s *valueScanner) scanOptional(v interface{}, defaultValueForOptional bool) {
if defaultValueForOptional {
if s.isNull() {
s.setDefaultValue(v)
@@ -1035,16 +1037,16 @@ func (s *scanner) scanOptional(v interface{}, defaultValueForOptional bool) {
src := s.any()
*v = &src
}
- case *types.Value:
+ case *value.Value:
*v = s.value()
- case **types.Decimal:
+ case **decimal.Decimal:
if s.isNull() {
*v = nil
} else {
src := s.unwrapDecimal()
*v = &src
}
- case types.Scanner:
+ case scanner.Scanner:
err := v.UnmarshalYDB(s.converter)
if err != nil {
_ = s.errorf(0, "ydb.Scanner error: %w", err)
@@ -1058,13 +1060,13 @@ func (s *scanner) scanOptional(v interface{}, defaultValueForOptional bool) {
s.unwrap()
var err error
switch s.getType() {
- case types.TypeJSON:
+ case internalTypes.JSON:
if s.isNull() {
err = v.UnmarshalJSON(nil)
} else {
err = v.UnmarshalJSON(s.converter.JSON())
}
- case types.TypeJSONDocument:
+ case internalTypes.JSONDocument:
if s.isNull() {
err = v.UnmarshalJSON(nil)
} else {
@@ -1090,7 +1092,7 @@ func (s *scanner) scanOptional(v interface{}, defaultValueForOptional bool) {
}
}
-func (s *scanner) setDefaultValue(dst interface{}) {
+func (s *valueScanner) setDefaultValue(dst interface{}) {
switch v := dst.(type) {
case *bool:
*v = false
@@ -1126,16 +1128,16 @@ func (s *scanner) setDefaultValue(dst interface{}) {
*v = [16]byte{}
case *interface{}:
*v = nil
- case *types.Value:
+ case *value.Value:
*v = s.value()
- case *types.Decimal:
- *v = types.Decimal{}
+ case *decimal.Decimal:
+ *v = decimal.Decimal{}
case sql.Scanner:
err := v.Scan(nil)
if err != nil {
_ = s.errorf(0, "sql.Scanner error: %w", err)
}
- case types.Scanner:
+ case scanner.Scanner:
err := v.UnmarshalYDB(s.converter)
if err != nil {
_ = s.errorf(0, "ydb.Scanner error: %w", err)
@@ -1159,7 +1161,7 @@ func (r *baseResult) SetErr(err error) {
})
}
-func (s *scanner) errorf(depth int, f string, args ...interface{}) error {
+func (s *valueScanner) errorf(depth int, f string, args ...interface{}) error {
s.errMtx.Lock()
defer s.errMtx.Unlock()
if s.err != nil {
@@ -1170,7 +1172,7 @@ func (s *scanner) errorf(depth int, f string, args ...interface{}) error {
return s.err
}
-func (s *scanner) typeError(act, exp interface{}) {
+func (s *valueScanner) typeError(act, exp interface{}) {
_ = s.errorf(
2, //nolint:gomnd
"unexpected types during scan at %q %s: %s; want %s",
@@ -1181,7 +1183,7 @@ func (s *scanner) typeError(act, exp interface{}) {
)
}
-func (s *scanner) valueTypeError(act, exp interface{}) {
+func (s *valueScanner) valueTypeError(act, exp interface{}) {
// unexpected value during scan at \"migration_status\" Int64: NullFlag; want Int64
_ = s.errorf(
2, //nolint:gomnd
@@ -1193,7 +1195,7 @@ func (s *scanner) valueTypeError(act, exp interface{}) {
)
}
-func (s *scanner) notFoundColumnByIndex(idx int) error {
+func (s *valueScanner) notFoundColumnByIndex(idx int) error {
return s.errorf(
2, //nolint:gomnd
"not found %d column",
@@ -1201,7 +1203,7 @@ func (s *scanner) notFoundColumnByIndex(idx int) error {
)
}
-func (s *scanner) notFoundColumnName(name string) error {
+func (s *valueScanner) notFoundColumnName(name string) error {
return s.errorf(
2, //nolint:gomnd
"not found column '%s'",
@@ -1209,7 +1211,7 @@ func (s *scanner) notFoundColumnName(name string) error {
)
}
-func (s *scanner) noColumnError(name string) error {
+func (s *valueScanner) noColumnError(name string) error {
return s.errorf(
2, //nolint:gomnd
"no column %q",
@@ -1217,7 +1219,7 @@ func (s *scanner) noColumnError(name string) error {
)
}
-func (s *scanner) overflowError(i, n interface{}) error {
+func (s *valueScanner) overflowError(i, n interface{}) error {
return s.errorf(
2, //nolint:gomnd
"overflow error: %d overflows capacity of %t",
@@ -1230,7 +1232,7 @@ var emptyItem item
type item struct {
name string
- i int // Index in listing types.
+ i int // Index in listing types
t *Ydb.Type
v *Ydb.Value
}
@@ -1307,7 +1309,7 @@ func (s *scanStack) current() item {
func (s *scanStack) currentValue() interface{} {
if v := s.current().v; v != nil {
- return v.Value
+ return v.GetValue()
}
return nil
@@ -1315,7 +1317,7 @@ func (s *scanStack) currentValue() interface{} {
func (s *scanStack) currentType() interface{} {
if t := s.current().t; t != nil {
- return t.Type
+ return t.GetType()
}
return nil
@@ -1325,7 +1327,7 @@ func isOptional(typ *Ydb.Type) bool {
if typ == nil {
return false
}
- _, yes := typ.Type.(*Ydb.Type_OptionalType)
+ _, yes := typ.GetType().(*Ydb.Type_OptionalType)
return yes
}
diff --git a/internal/table/scanner/scanner_data_test.go b/internal/table/scanner/scanner_data_test.go
index 396dd3d28..f47fffbac 100644
--- a/internal/table/scanner/scanner_data_test.go
+++ b/internal/table/scanner/scanner_data_test.go
@@ -208,7 +208,7 @@ var scannerData = []struct {
setColumnIndexes: []int{0, 2, 1},
},
{
- name: "Scan int64, float, json as ydb.Value",
+ name: "Scan int64, float, json as ydb.valueType",
count: 100,
columns: []*column{{
name: "valueint64",
@@ -470,8 +470,8 @@ var scannerData = []struct {
},
}
-func initScanner() *scanner {
- res := scanner{
+func initScanner() *valueScanner {
+ res := valueScanner{
set: &Ydb.ResultSet{
Columns: nil,
Rows: nil,
@@ -491,7 +491,7 @@ func initScanner() *scanner {
return &res
}
-func PrepareScannerPerformanceTest(count int) *scanner {
+func PrepareScannerPerformanceTest(count int) *valueScanner {
res := initScanner()
res.set.Columns = []*Ydb.Column{{
Name: "series_id",
@@ -529,7 +529,7 @@ func PrepareScannerPerformanceTest(count int) *scanner {
}}
res.set.Rows = []*Ydb.Value{}
for i := 0; i < count; i++ {
- res.set.Rows = append(res.set.Rows, &Ydb.Value{
+ res.set.Rows = append(res.set.GetRows(), &Ydb.Value{
Items: []*Ydb.Value{{
Value: &Ydb.Value_Uint64Value{
Uint64Value: uint64(i),
diff --git a/internal/table/scanner/scanner_test.go b/internal/table/scanner/scanner_test.go
index cc595229a..44f09ff45 100644
--- a/internal/table/scanner/scanner_test.go
+++ b/internal/table/scanner/scanner_test.go
@@ -530,7 +530,7 @@ func getResultSet(count int, col []*column) (result *Ydb.ResultSet, testValues [
}
}
result.Columns = append(
- result.Columns,
+ result.GetColumns(),
&Ydb.Column{
Name: c.name,
Type: t,
@@ -543,12 +543,12 @@ func getResultSet(count int, col []*column) (result *Ydb.ResultSet, testValues [
for i := 0; i < count; i++ {
var items []*Ydb.Value
var vals []indexed.RequiredOrOptional
- for j := range result.Columns {
+ for j := range result.GetColumns() {
v, val := valueFromPrimitiveTypeID(col[j], r)
vals = append(vals, val)
items = append(items, v)
}
- result.Rows = append(result.Rows, &Ydb.Value{
+ result.Rows = append(result.GetRows(), &Ydb.Value{
Items: items,
})
testValues[i] = vals
diff --git a/internal/table/scanner/stats.go b/internal/table/scanner/stats.go
index 250205c5b..d9772e34c 100644
--- a/internal/table/scanner/stats.go
+++ b/internal/table/scanner/stats.go
@@ -20,14 +20,14 @@ func (s *queryStats) ProcessCPUTime() time.Duration {
}
func (s *queryStats) Compilation() (c *stats.CompilationStats) {
- if s.stats == nil || s.stats.Compilation == nil {
+ if s.stats == nil || s.stats.GetCompilation() == nil {
return nil
}
return &stats.CompilationStats{
- FromCache: s.stats.Compilation.FromCache,
- Duration: time.Microsecond * time.Duration(s.stats.Compilation.DurationUs),
- CPUTime: time.Microsecond * time.Duration(s.stats.Compilation.CpuTimeUs),
+ FromCache: s.stats.GetCompilation().GetFromCache(),
+ Duration: time.Microsecond * time.Duration(s.stats.GetCompilation().GetDurationUs()),
+ CPUTime: time.Microsecond * time.Duration(s.stats.GetCompilation().GetCpuTimeUs()),
}
}
@@ -40,32 +40,32 @@ func (s *queryStats) QueryAST() string {
}
func (s *queryStats) TotalCPUTime() time.Duration {
- return time.Microsecond * time.Duration(s.stats.TotalCpuTimeUs)
+ return time.Microsecond * time.Duration(s.stats.GetTotalCpuTimeUs())
}
func (s *queryStats) TotalDuration() time.Duration {
- return time.Microsecond * time.Duration(s.stats.TotalDurationUs)
+ return time.Microsecond * time.Duration(s.stats.GetTotalDurationUs())
}
// NextPhase returns next execution phase within query.
// If ok flag is false, then there are no more phases and p is invalid.
func (s *queryStats) NextPhase() (p stats.QueryPhase, ok bool) {
- if s.pos >= len(s.stats.QueryPhases) {
+ if s.pos >= len(s.stats.GetQueryPhases()) {
return
}
- x := s.stats.QueryPhases[s.pos]
+ x := s.stats.GetQueryPhases()[s.pos]
if x == nil {
return
}
s.pos++
return &queryPhase{
- tables: x.TableAccess,
+ tables: x.GetTableAccess(),
pos: 0,
- duration: time.Microsecond * time.Duration(x.DurationUs),
- cpuTime: time.Microsecond * time.Duration(x.CpuTimeUs),
- affectedShards: x.AffectedShards,
- literalPhase: x.LiteralPhase,
+ duration: time.Microsecond * time.Duration(x.GetDurationUs()),
+ cpuTime: time.Microsecond * time.Duration(x.GetCpuTimeUs()),
+ affectedShards: x.GetAffectedShards(),
+ literalPhase: x.GetLiteralPhase(),
}, true
}
@@ -91,10 +91,10 @@ func (q *queryPhase) NextTableAccess() (t *stats.TableAccess, ok bool) {
q.pos++
return &stats.TableAccess{
- Name: x.Name,
- Reads: initOperationStats(x.Reads),
- Updates: initOperationStats(x.Updates),
- Deletes: initOperationStats(x.Deletes),
+ Name: x.GetName(),
+ Reads: initOperationStats(x.GetReads()),
+ Updates: initOperationStats(x.GetUpdates()),
+ Deletes: initOperationStats(x.GetDeletes()),
}, true
}
@@ -120,7 +120,7 @@ func initOperationStats(x *Ydb_TableStats.OperationStats) stats.OperationStats {
}
return stats.OperationStats{
- Rows: x.Rows,
- Bytes: x.Bytes,
+ Rows: x.GetRows(),
+ Bytes: x.GetBytes(),
}
}
diff --git a/internal/table/session.go b/internal/table/session.go
index 121d58f14..3c9fe585c 100644
--- a/internal/table/session.go
+++ b/internal/table/session.go
@@ -22,9 +22,11 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/feature"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/meta"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
@@ -32,7 +34,6 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
@@ -44,17 +45,15 @@ import (
// Note that after session is no longer needed it should be destroyed by
// Close() call.
type session struct {
+ onClose []func(s *session)
id string
tableService Ydb_Table_V1.TableServiceClient
+ status table.SessionStatus
config *config.Config
-
- status table.SessionStatus
- statusMtx sync.RWMutex
- nodeID atomic.Uint32
- lastUsage atomic.Int64
-
- onClose []func(s *session)
- closeOnce sync.Once
+ lastUsage atomic.Int64
+ statusMtx sync.RWMutex
+ closeOnce sync.Once
+ nodeID atomic.Uint32
}
func (s *session) LastUsage() time.Time {
@@ -117,7 +116,9 @@ func (s *session) isClosing() bool {
func newSession(ctx context.Context, cc grpc.ClientConnInterface, config *config.Config) (
s *session, err error,
) {
- onDone := trace.TableOnSessionNew(config.Trace(), &ctx, stack.FunctionID(""))
+ onDone := trace.TableOnSessionNew(config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.newSession"),
+ )
defer func() {
onDone(s, err)
}()
@@ -180,7 +181,7 @@ func (s *session) Close(ctx context.Context) (err error) {
s.closeOnce.Do(func() {
onDone := trace.TableOnSessionDelete(s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).Close"),
s,
)
defer func() {
@@ -232,7 +233,7 @@ func (s *session) KeepAlive(ctx context.Context) (err error) {
result Ydb_Table.KeepAliveResult
onDone = trace.TableOnSessionKeepAlive(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).KeepAlive"),
s,
)
)
@@ -260,7 +261,7 @@ func (s *session) KeepAlive(ctx context.Context) (err error) {
return xerrors.WithStackTrace(err)
}
- switch result.SessionStatus {
+ switch result.GetSessionStatus() {
case Ydb_Table.KeepAliveResult_SESSION_STATUS_READY:
s.SetStatus(table.SessionReady)
case Ydb_Table.KeepAliveResult_SESSION_STATUS_BUSY:
@@ -341,10 +342,10 @@ func (s *session) DescribeTable(
[]options.Column,
len(result.GetColumns()),
)
- for i, c := range result.Columns {
+ for i, c := range result.GetColumns() {
cs[i] = options.Column{
Name: c.GetName(),
- Type: value.TypeFromYDB(c.GetType()),
+ Type: types.TypeFromYDB(c.GetType()),
Family: c.GetFamily(),
}
}
@@ -353,7 +354,7 @@ func (s *session) DescribeTable(
[]options.KeyRange,
len(result.GetShardKeyBounds())+1,
)
- var last types.Value
+ var last value.Value
for i, b := range result.GetShardKeyBounds() {
if last != nil {
rs[i].From = last
@@ -376,18 +377,18 @@ func (s *session) DescribeTable(
[]options.PartitionStats,
len(result.GetTableStats().GetPartitionStats()),
)
- for i, v := range result.TableStats.PartitionStats {
+ for i, v := range result.GetTableStats().GetPartitionStats() {
partStats[i].RowsEstimate = v.GetRowsEstimate()
partStats[i].StoreSize = v.GetStoreSize()
}
var creationTime, modificationTime time.Time
- if resStats.CreationTime.GetSeconds() != 0 {
+ if resStats.GetCreationTime().GetSeconds() != 0 {
creationTime = time.Unix(
resStats.GetCreationTime().GetSeconds(),
int64(resStats.GetCreationTime().GetNanos()),
)
}
- if resStats.ModificationTime.GetSeconds() != 0 {
+ if resStats.GetModificationTime().GetSeconds() != 0 {
modificationTime = time.Unix(
resStats.GetModificationTime().GetSeconds(),
int64(resStats.GetModificationTime().GetNanos()),
@@ -414,10 +415,10 @@ func (s *session) DescribeTable(
attrs[k] = v
}
- indexes := make([]options.IndexDescription, len(result.Indexes))
+ indexes := make([]options.IndexDescription, len(result.GetIndexes()))
for i, idx := range result.GetIndexes() {
var typ options.IndexType
- switch idx.Type.(type) {
+ switch idx.GetType().(type) {
case *Ydb_Table.TableIndexDescription_GlobalAsyncIndex:
typ = options.IndexTypeGlobalAsync
case *Ydb_Table.TableIndexDescription_GlobalIndex:
@@ -432,7 +433,7 @@ func (s *session) DescribeTable(
}
}
- changeFeeds := make([]options.ChangefeedDescription, len(result.Changefeeds))
+ changeFeeds := make([]options.ChangefeedDescription, len(result.GetChangefeeds()))
for i, proto := range result.GetChangefeeds() {
changeFeeds[i] = options.NewChangefeedDescription(proto)
}
@@ -486,7 +487,7 @@ func (s *session) checkError(err error) {
if err == nil {
return
}
- if m := retry.Check(err); m.MustDeleteSession() {
+ if m := retry.Check(err); m.IsRetryObjectValid() {
s.SetStatus(table.SessionClosing)
}
}
@@ -577,7 +578,7 @@ func copyTables(
opt((*options.CopyTablesDesc)(&request))
}
}
- if len(request.Tables) == 0 {
+ if len(request.GetTables()) == 0 {
return xerrors.WithStackTrace(fmt.Errorf("no CopyTablesItem: %w", errParamsRequired))
}
_, err = service.CopyTables(ctx, &request)
@@ -614,7 +615,7 @@ func (s *session) Explain(
response *Ydb_Table.ExplainDataQueryResponse
onDone = trace.TableOnSessionQueryExplain(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).Explain"),
s, query,
)
)
@@ -651,7 +652,7 @@ func (s *session) Explain(
Explanation: table.Explanation{
Plan: result.GetQueryPlan(),
},
- AST: result.QueryAst,
+ AST: result.GetQueryAst(),
}, nil
}
@@ -663,7 +664,7 @@ func (s *session) Prepare(ctx context.Context, queryText string) (_ table.Statem
result Ydb_Table.PrepareQueryResult
onDone = trace.TableOnSessionQueryPrepare(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).Prepare"),
s, queryText,
)
)
@@ -699,7 +700,7 @@ func (s *session) Prepare(ctx context.Context, queryText string) (_ table.Statem
stmt = &statement{
session: s,
query: queryPrepared(result.GetQueryId(), queryText),
- params: result.ParametersTypes,
+ params: result.GetParametersTypes(),
}
return stmt, nil
@@ -710,7 +711,7 @@ func (s *session) Execute(
ctx context.Context,
txControl *table.TransactionControl,
query string,
- params *table.QueryParameters,
+ parameters *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (
txr table.Transaction, r result.Result, err error,
@@ -728,10 +729,10 @@ func (s *session) Execute(
request.SessionId = s.id
request.TxControl = txControl.Desc()
- request.Parameters = params.Params().ToYDB(a)
+ request.Parameters = parameters.ToYDB(a)
request.Query = q.toYDB(a)
request.QueryCachePolicy = a.TableQueryCachePolicy()
- request.QueryCachePolicy.KeepInCache = len(params.Params()) > 0
+ request.QueryCachePolicy.KeepInCache = len(request.Parameters) > 0
request.OperationParams = operation.Params(ctx,
s.config.OperationTimeout(),
s.config.OperationCancelAfter(),
@@ -746,8 +747,8 @@ func (s *session) Execute(
onDone := trace.TableOnSessionQueryExecute(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
- s, q, params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).Execute"),
+ s, q, parameters,
request.QueryCachePolicy.GetKeepInCache(),
)
defer func() {
@@ -775,7 +776,7 @@ func (s *session) executeQueryResult(
id: res.GetTxMeta().GetId(),
s: s,
}
- if txControl.CommitTx {
+ if txControl.GetCommitTx() {
tx.state.Store(txStateCommitted)
} else {
tx.state.Store(txStateInitialized)
@@ -985,8 +986,8 @@ func (s *session) StreamReadTable(
opts ...options.ReadTableOption,
) (_ result.StreamResult, err error) {
var (
- onIntermediate = trace.TableOnSessionQueryStreamRead(s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ onDone = trace.TableOnSessionQueryStreamRead(s.config.Trace(), &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).StreamReadTable"),
s,
)
request = Ydb_Table.ReadTableRequest{
@@ -998,9 +999,7 @@ func (s *session) StreamReadTable(
)
defer func() {
a.Free()
- if err != nil {
- onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
- }
+ onDone(xerrors.HideEOF(err))
}()
for _, opt := range opts {
@@ -1024,9 +1023,6 @@ func (s *session) StreamReadTable(
stats *Ydb_TableStats.QueryStats,
err error,
) {
- defer func() {
- onIntermediate(xerrors.HideEOF(err))
- }()
select {
case <-ctx.Done():
return nil, nil, xerrors.WithStackTrace(ctx.Err())
@@ -1043,7 +1039,7 @@ func (s *session) StreamReadTable(
},
func(err error) error {
cancel()
- onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
+ onDone(xerrors.HideEOF(err))
return err
},
@@ -1054,7 +1050,7 @@ func (s *session) StreamReadTable(
func (s *session) ReadRows(
ctx context.Context,
path string,
- keys types.Value,
+ keys value.Value,
opts ...options.ReadRowsOption,
) (_ result.Result, err error) {
var (
@@ -1102,20 +1098,20 @@ func (s *session) ReadRows(
func (s *session) StreamExecuteScanQuery(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ parameters *params.Parameters,
opts ...options.ExecuteScanQueryOption,
) (_ result.StreamResult, err error) {
var (
- a = allocator.New()
- q = queryFromText(query)
- onIntermediate = trace.TableOnSessionQueryStreamExecute(
+ a = allocator.New()
+ q = queryFromText(query)
+ onDone = trace.TableOnSessionQueryStreamExecute(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
- s, q, params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).StreamExecuteScanQuery"),
+ s, q, parameters,
)
request = Ydb_Table.ExecuteScanQueryRequest{
Query: q.toYDB(a),
- Parameters: params.Params().ToYDB(a),
+ Parameters: parameters.ToYDB(a),
Mode: Ydb_Table.ExecuteScanQueryRequest_MODE_EXEC, // set default
}
stream Ydb_Table_V1.TableService_StreamExecuteScanQueryClient
@@ -1123,9 +1119,7 @@ func (s *session) StreamExecuteScanQuery(
)
defer func() {
a.Free()
- if err != nil {
- onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
- }
+ onDone(xerrors.HideEOF(err))
}()
for _, opt := range opts {
@@ -1149,9 +1143,6 @@ func (s *session) StreamExecuteScanQuery(
stats *Ydb_TableStats.QueryStats,
err error,
) {
- defer func() {
- onIntermediate(xerrors.HideEOF(err))
- }()
select {
case <-ctx.Done():
return nil, nil, xerrors.WithStackTrace(ctx.Err())
@@ -1168,7 +1159,7 @@ func (s *session) StreamExecuteScanQuery(
},
func(err error) error {
cancel()
- onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err))
+ onDone(xerrors.HideEOF(err))
return err
},
@@ -1178,7 +1169,7 @@ func (s *session) StreamExecuteScanQuery(
}
// BulkUpsert uploads given list of ydb struct values to the table.
-func (s *session) BulkUpsert(ctx context.Context, table string, rows types.Value,
+func (s *session) BulkUpsert(ctx context.Context, table string, rows value.Value,
opts ...options.BulkUpsertOption,
) (err error) {
var (
@@ -1186,7 +1177,7 @@ func (s *session) BulkUpsert(ctx context.Context, table string, rows types.Value
callOptions []grpc.CallOption
onDone = trace.TableOnSessionBulkUpsert(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).BulkUpsert"),
s,
)
)
@@ -1196,7 +1187,9 @@ func (s *session) BulkUpsert(ctx context.Context, table string, rows types.Value
}()
for _, opt := range opts {
- callOptions = append(callOptions, opt.ApplyBulkUpsertOption()...)
+ if opt != nil {
+ callOptions = append(callOptions, opt.ApplyBulkUpsertOption()...)
+ }
}
_, err = s.tableService.BulkUpsert(ctx,
@@ -1228,9 +1221,9 @@ func (s *session) BeginTransaction(
var (
result Ydb_Table.BeginTransactionResult
response *Ydb_Table.BeginTransactionResponse
- onDone = trace.TableOnSessionTransactionBegin(
+ onDone = trace.TableOnTxBegin(
s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*session).BeginTransaction"),
s,
)
)
diff --git a/internal/table/session_test.go b/internal/table/session_test.go
index cd581a9fd..006086369 100644
--- a/internal/table/session_test.go
+++ b/internal/table/session_test.go
@@ -22,12 +22,12 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
"github.com/ydb-platform/ydb-go-sdk/v3/testutil"
)
@@ -133,17 +133,17 @@ func TestSessionDescribeTable(t *testing.T) {
Columns: []options.Column{
{
Name: "testColumn",
- Type: types.Void(),
+ Type: types.NewVoid(),
Family: "testFamily",
},
},
KeyRanges: []options.KeyRange{
{
From: nil,
- To: types.Int64Value(100500),
+ To: value.Int64Value(100500),
},
{
- From: types.Int64Value(100500),
+ From: value.Int64Value(100500),
To: nil,
},
},
@@ -182,7 +182,7 @@ func TestSessionDescribeTable(t *testing.T) {
Columns: []*Ydb_Table.ColumnMeta{
{
Name: expect.Columns[0].Name,
- Type: value.TypeToYDB(expect.Columns[0].Type, a),
+ Type: types.TypeToYDB(expect.Columns[0].Type, a),
Family: "testFamily",
},
},
@@ -339,7 +339,7 @@ func TestSessionOperationModeOnExecuteDataQuery(t *testing.T) {
func(t *testing.T) {
for _, srcDst := range fromTo {
t.Run(srcDst.srcMode.String()+"->"+srcDst.dstMode.String(), func(t *testing.T) {
- client, err := New(context.Background(), testutil.NewBalancer(
+ client := New(context.Background(), testutil.NewBalancer(
testutil.WithInvokeHandlers(
testutil.InvokeHandlers{
testutil.TableExecuteDataQuery: func(interface{}) (proto.Message, error) {
@@ -382,7 +382,6 @@ func TestSessionOperationModeOnExecuteDataQuery(t *testing.T) {
},
),
), config.New())
- require.NoError(t, err)
ctx, cancel := xcontext.WithTimeout(
context.Background(),
time.Second,
@@ -397,7 +396,7 @@ func TestSessionOperationModeOnExecuteDataQuery(t *testing.T) {
}
func TestCreateTableRegression(t *testing.T) {
- client, err := New(context.Background(), testutil.NewBalancer(
+ client := New(context.Background(), testutil.NewBalancer(
testutil.WithInvokeHandlers(
testutil.InvokeHandlers{
testutil.TableCreateSession: func(request interface{}) (proto.Message, error) {
@@ -474,21 +473,19 @@ func TestCreateTableRegression(t *testing.T) {
),
), config.New())
- require.NoError(t, err)
-
ctx, cancel := xcontext.WithTimeout(
context.Background(),
time.Second,
)
defer cancel()
- err = client.Do(ctx, func(ctx context.Context, s table.Session) error {
+ err := client.Do(ctx, func(ctx context.Context, s table.Session) error {
return s.CreateTable(ctx, "episodes",
- options.WithColumn("series_id", types.Optional(types.TypeUint64)),
- options.WithColumn("season_id", types.Optional(types.TypeUint64)),
- options.WithColumn("episode_id", types.Optional(types.TypeUint64)),
- options.WithColumn("title", types.Optional(types.TypeText)),
- options.WithColumn("air_date", types.Optional(types.TypeUint64)),
+ options.WithColumn("series_id", types.NewOptional(types.Uint64)),
+ options.WithColumn("season_id", types.NewOptional(types.Uint64)),
+ options.WithColumn("episode_id", types.NewOptional(types.Uint64)),
+ options.WithColumn("title", types.NewOptional(types.Text)),
+ options.WithColumn("air_date", types.NewOptional(types.Uint64)),
options.WithPrimaryKeyColumn("series_id", "season_id", "episode_id"),
options.WithAttribute("attr", "attr_value"),
)
@@ -498,7 +495,7 @@ func TestCreateTableRegression(t *testing.T) {
}
func TestDescribeTableRegression(t *testing.T) {
- client, err := New(context.Background(), testutil.NewBalancer(
+ client := New(context.Background(), testutil.NewBalancer(
testutil.WithInvokeHandlers(
testutil.InvokeHandlers{
testutil.TableCreateSession: func(request interface{}) (proto.Message, error) {
@@ -567,8 +564,6 @@ func TestDescribeTableRegression(t *testing.T) {
),
), config.New())
- require.NoError(t, err)
-
ctx, cancel := xcontext.WithTimeout(
context.Background(),
time.Second,
@@ -577,7 +572,7 @@ func TestDescribeTableRegression(t *testing.T) {
var act options.Description
- err = client.Do(ctx, func(ctx context.Context, s table.Session) (err error) {
+ err := client.Do(ctx, func(ctx context.Context, s table.Session) (err error) {
act, err = s.DescribeTable(ctx, "episodes")
return err
@@ -590,23 +585,23 @@ func TestDescribeTableRegression(t *testing.T) {
Columns: []options.Column{
{
Name: "series_id",
- Type: types.Optional(types.TypeUint64),
+ Type: types.NewOptional(types.Uint64),
},
{
Name: "season_id",
- Type: types.Optional(types.TypeUint64),
+ Type: types.NewOptional(types.Uint64),
},
{
Name: "episode_id",
- Type: types.Optional(types.TypeUint64),
+ Type: types.NewOptional(types.Uint64),
},
{
Name: "title",
- Type: types.Optional(types.TypeText),
+ Type: types.NewOptional(types.Text),
},
{
Name: "air_date",
- Type: types.Optional(types.TypeUint64),
+ Type: types.NewOptional(types.Uint64),
},
},
KeyRanges: []options.KeyRange{
diff --git a/internal/table/statement.go b/internal/table/statement.go
index d0cf27c54..eb797e2ef 100644
--- a/internal/table/statement.go
+++ b/internal/table/statement.go
@@ -9,6 +9,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
@@ -26,7 +27,7 @@ type statement struct {
// Execute executes prepared data query.
func (s *statement) Execute(
ctx context.Context, txControl *table.TransactionControl,
- params *table.QueryParameters,
+ parameters *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (
txr table.Transaction, r result.Result, err error,
@@ -43,10 +44,10 @@ func (s *statement) Execute(
request.SessionId = s.session.id
request.TxControl = txControl.Desc()
- request.Parameters = params.Params().ToYDB(a)
+ request.Parameters = parameters.ToYDB(a)
request.Query = s.query.toYDB(a)
request.QueryCachePolicy = a.TableQueryCachePolicy()
- request.QueryCachePolicy.KeepInCache = len(params.Params()) > 0
+ request.QueryCachePolicy.KeepInCache = len(request.Parameters) > 0
request.OperationParams = operation.Params(ctx,
s.session.config.OperationTimeout(),
s.session.config.OperationCancelAfter(),
@@ -61,8 +62,8 @@ func (s *statement) Execute(
onDone := trace.TableOnSessionQueryExecute(
s.session.config.Trace(), &ctx,
- stack.FunctionID(""),
- s.session, s.query, params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*statement).Execute"),
+ s.session, s.query, parameters,
request.QueryCachePolicy.GetKeepInCache(),
)
defer func() {
diff --git a/internal/table/transaction.go b/internal/table/transaction.go
index 90f5bd21b..a07576196 100644
--- a/internal/table/transaction.go
+++ b/internal/table/transaction.go
@@ -9,6 +9,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/operation"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
@@ -57,13 +58,13 @@ func (tx *transaction) ID() string {
// Execute executes query represented by text within transaction tx.
func (tx *transaction) Execute(
ctx context.Context,
- query string, params *table.QueryParameters,
+ query string, parameters *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (r result.Result, err error) {
- onDone := trace.TableOnSessionTransactionExecute(
+ onDone := trace.TableOnTxExecute(
tx.s.config.Trace(), &ctx,
- stack.FunctionID(""),
- tx.s, tx, queryFromText(query), params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*transaction).Execute"),
+ tx.s, tx, queryFromText(query), parameters,
)
defer func() {
onDone(r, err)
@@ -75,12 +76,12 @@ func (tx *transaction) Execute(
case txStateRollbacked:
return nil, xerrors.WithStackTrace(errTxRollbackedEarly)
default:
- _, r, err = tx.s.Execute(ctx, tx.control, query, params, opts...)
+ _, r, err = tx.s.Execute(ctx, tx.control, query, parameters, opts...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
- if tx.control.Desc().CommitTx {
+ if tx.control.Desc().GetCommitTx() {
tx.state.Store(txStateCommitted)
}
@@ -91,19 +92,16 @@ func (tx *transaction) Execute(
// ExecuteStatement executes prepared statement stmt within transaction tx.
func (tx *transaction) ExecuteStatement(
ctx context.Context,
- stmt table.Statement, params *table.QueryParameters,
+ stmt table.Statement, parameters *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (r result.Result, err error) {
- if params == nil {
- params = table.NewQueryParameters()
- }
a := allocator.New()
defer a.Free()
- onDone := trace.TableOnSessionTransactionExecuteStatement(
+ onDone := trace.TableOnTxExecuteStatement(
tx.s.config.Trace(), &ctx,
- stack.FunctionID(""),
- tx.s, tx, stmt.(*statement).query, params,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*transaction).ExecuteStatement"),
+ tx.s, tx, stmt.(*statement).query, parameters,
)
defer func() {
onDone(r, err)
@@ -115,12 +113,12 @@ func (tx *transaction) ExecuteStatement(
case txStateRollbacked:
return nil, xerrors.WithStackTrace(errTxRollbackedEarly)
default:
- _, r, err = stmt.Execute(ctx, tx.control, params, opts...)
+ _, r, err = stmt.Execute(ctx, tx.control, parameters, opts...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
- if tx.control.Desc().CommitTx {
+ if tx.control.Desc().GetCommitTx() {
tx.state.Store(txStateCommitted)
}
@@ -133,9 +131,9 @@ func (tx *transaction) CommitTx(
ctx context.Context,
opts ...options.CommitTransactionOption,
) (r result.Result, err error) {
- onDone := trace.TableOnSessionTransactionCommit(
+ onDone := trace.TableOnTxCommit(
tx.s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*transaction).CommitTx"),
tx.s, tx,
)
defer func() {
@@ -191,9 +189,9 @@ func (tx *transaction) CommitTx(
// Rollback performs a rollback of the specified active transaction.
func (tx *transaction) Rollback(ctx context.Context) (err error) {
- onDone := trace.TableOnSessionTransactionRollback(
+ onDone := trace.TableOnTxRollback(
tx.s.config.Trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/table.(*transaction).Rollback"),
tx.s, tx,
)
defer func() {
diff --git a/internal/table/ttl.go b/internal/table/ttl.go
index a91e9729f..fd3ad945e 100644
--- a/internal/table/ttl.go
+++ b/internal/table/ttl.go
@@ -10,19 +10,19 @@ func NewTimeToLiveSettings(settings *Ydb_Table.TtlSettings) *options.TimeToLiveS
if settings == nil {
return nil
}
- switch mode := settings.Mode.(type) {
+ switch mode := settings.GetMode().(type) {
case *Ydb_Table.TtlSettings_DateTypeColumn:
return &options.TimeToLiveSettings{
- ColumnName: mode.DateTypeColumn.ColumnName,
- ExpireAfterSeconds: mode.DateTypeColumn.ExpireAfterSeconds,
+ ColumnName: mode.DateTypeColumn.GetColumnName(),
+ ExpireAfterSeconds: mode.DateTypeColumn.GetExpireAfterSeconds(),
Mode: options.TimeToLiveModeDateType,
}
case *Ydb_Table.TtlSettings_ValueSinceUnixEpoch:
return &options.TimeToLiveSettings{
- ColumnName: mode.ValueSinceUnixEpoch.ColumnName,
- ColumnUnit: timeToLiveUnit(mode.ValueSinceUnixEpoch.ColumnUnit),
- ExpireAfterSeconds: mode.ValueSinceUnixEpoch.ExpireAfterSeconds,
+ ColumnName: mode.ValueSinceUnixEpoch.GetColumnName(),
+ ColumnUnit: timeToLiveUnit(mode.ValueSinceUnixEpoch.GetColumnUnit()),
+ ExpireAfterSeconds: mode.ValueSinceUnixEpoch.GetExpireAfterSeconds(),
Mode: options.TimeToLiveModeValueSinceUnixEpoch,
}
}
diff --git a/internal/topic/retriable_error.go b/internal/topic/retriable_error.go
index aebcd44f8..744aca97a 100644
--- a/internal/topic/retriable_error.go
+++ b/internal/topic/retriable_error.go
@@ -13,7 +13,7 @@ import (
)
const (
- DefaultStartTimeout = time.Minute
+ DefaultStartTimeout = value.InfiniteDuration
connectionEstablishedTimeout = time.Minute
)
diff --git a/internal/topic/topicclientinternal/client.go b/internal/topic/topicclientinternal/client.go
index a023b1471..2318d97b1 100644
--- a/internal/topic/topicclientinternal/client.go
+++ b/internal/topic/topicclientinternal/client.go
@@ -32,7 +32,7 @@ func New(
conn grpc.ClientConnInterface,
cred credentials.Credentials,
opts ...topicoptions.TopicOption,
-) (*Client, error) {
+) *Client {
rawClient := rawtopic.NewClient(Ydb_Topic_V1.NewTopicServiceClient(conn))
cfg := newTopicConfig(opts...)
@@ -45,16 +45,16 @@ func New(
cred: cred,
defaultOperationParams: defaultOperationParams,
rawClient: rawClient,
- }, nil
+ }
}
func newTopicConfig(opts ...topicoptions.TopicOption) topic.Config {
c := topic.Config{
Trace: &trace.Topic{},
}
- for _, o := range opts {
- if o != nil {
- o(&c)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&c)
}
}
@@ -71,9 +71,9 @@ func (c *Client) Alter(ctx context.Context, path string, opts ...topicoptions.Al
req := &rawtopic.AlterTopicRequest{}
req.OperationParams = c.defaultOperationParams
req.Path = path
- for _, o := range opts {
- if o != nil {
- o.ApplyAlterOption(req)
+ for _, opt := range opts {
+ if opt != nil {
+ opt.ApplyAlterOption(req)
}
}
@@ -103,9 +103,9 @@ func (c *Client) Create(
req.OperationParams = c.defaultOperationParams
req.Path = path
- for _, o := range opts {
- if o != nil {
- o.ApplyCreateOption(req)
+ for _, opt := range opts {
+ if opt != nil {
+ opt.ApplyCreateOption(req)
}
}
@@ -136,9 +136,9 @@ func (c *Client) Describe(
Path: path,
}
- for _, o := range opts {
- if o != nil {
- o(&req)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&req)
}
}
@@ -176,9 +176,9 @@ func (c *Client) Drop(ctx context.Context, path string, opts ...topicoptions.Dro
req.OperationParams = c.defaultOperationParams
req.Path = path
- for _, o := range opts {
- if o != nil {
- o.ApplyDropOption(&req)
+ for _, opt := range opts {
+ if opt != nil {
+ opt.ApplyDropOption(&req)
}
}
diff --git a/internal/topic/topicreaderinternal/batcher.go b/internal/topic/topicreaderinternal/batcher.go
index deebbb209..8eaa5eb6b 100644
--- a/internal/topic/topicreaderinternal/batcher.go
+++ b/internal/topic/topicreaderinternal/batcher.go
@@ -176,7 +176,7 @@ func (b *batcher) Pop(ctx context.Context, opts batcherGetOptions) (_ batcherMes
findRes = b.findNeedLock(opts)
if findRes.Ok {
- b.applyNeedLock(findRes)
+ b.applyNeedLock(&findRes)
return
}
@@ -279,7 +279,7 @@ func (b *batcher) applyForceFlagToOptions(options batcherGetOptions) batcherGetO
return res
}
-func (b *batcher) applyNeedLock(res batcherResultCandidate) {
+func (b *batcher) applyNeedLock(res *batcherResultCandidate) {
if res.Rest.IsEmpty() && res.WaiterIndex >= 0 {
delete(b.messages, res.Key)
} else {
diff --git a/internal/topic/topicreaderinternal/batcher_test.go b/internal/topic/topicreaderinternal/batcher_test.go
index 1bcdb33ef..c926e7076 100644
--- a/internal/topic/topicreaderinternal/batcher_test.go
+++ b/internal/topic/topicreaderinternal/batcher_test.go
@@ -407,7 +407,7 @@ func TestBatcher_Apply(t *testing.T) {
Key: session,
Rest: batcherMessageOrderItems{newBatcherItemBatch(batch)},
}
- b.applyNeedLock(foundRes)
+ b.applyNeedLock(&foundRes)
expectedMap := batcherMessagesMap{session: batcherMessageOrderItems{newBatcherItemBatch(batch)}}
require.Equal(t, expectedMap, b.messages)
@@ -426,7 +426,7 @@ func TestBatcher_Apply(t *testing.T) {
b.messages = batcherMessagesMap{session: batcherMessageOrderItems{newBatcherItemBatch(batch)}}
- b.applyNeedLock(foundRes)
+ b.applyNeedLock(&foundRes)
require.Empty(t, b.messages)
})
diff --git a/internal/topic/topicreaderinternal/committer.go b/internal/topic/topicreaderinternal/committer.go
index acb9e5b27..f0fa1c425 100644
--- a/internal/topic/topicreaderinternal/committer.go
+++ b/internal/topic/topicreaderinternal/committer.go
@@ -166,7 +166,10 @@ func (c *committer) waitSendTrigger(ctx context.Context) {
return
}
- finish := c.clock.After(c.BufferTimeLagTrigger)
+ bufferTimeLagTriggerTimer := c.clock.NewTimer(c.BufferTimeLagTrigger)
+ defer bufferTimeLagTriggerTimer.Stop()
+
+ finish := bufferTimeLagTriggerTimer.Chan()
if c.BufferCountTrigger == 0 {
select {
case <-ctxDone:
diff --git a/internal/topic/topicreaderinternal/decoders.go b/internal/topic/topicreaderinternal/decoders.go
index a4598192c..87773cdc7 100644
--- a/internal/topic/topicreaderinternal/decoders.go
+++ b/internal/topic/topicreaderinternal/decoders.go
@@ -36,7 +36,7 @@ func (m *decoderMap) Decode(codec rawtopiccommon.Codec, input io.Reader) (io.Rea
}
return nil, xerrors.WithStackTrace(xerrors.Wrap(
- fmt.Errorf("ydb: failed decompress message with codec %v: %w", codec, PublicErrUnexpectedCodec),
+ fmt.Errorf("ydb: failed decompress message with codec %v: %w", codec, ErrPublicUnexpectedCodec),
))
}
diff --git a/internal/topic/topicreaderinternal/message.go b/internal/topic/topicreaderinternal/message.go
index 7e5818eaf..13cc98f0c 100644
--- a/internal/topic/topicreaderinternal/message.go
+++ b/internal/topic/topicreaderinternal/message.go
@@ -14,8 +14,8 @@ import (
var errMessageWasReadEarly = xerrors.Wrap(errors.New("ydb: message was read early"))
-// PublicErrUnexpectedCodec return when try to read message content with unknown codec
-var PublicErrUnexpectedCodec = errors.New("unexpected codec") //nolint:revive,stylecheck
+// ErrPublicUnexpectedCodec return when try to read message content with unknown codec
+var ErrPublicUnexpectedCodec = errors.New("unexpected codec")
// PublicMessage is representation of topic message
type PublicMessage struct {
diff --git a/internal/topic/topicreaderinternal/reader.go b/internal/topic/topicreaderinternal/reader.go
index 030a5ca4b..b499221d1 100644
--- a/internal/topic/topicreaderinternal/reader.go
+++ b/internal/topic/topicreaderinternal/reader.go
@@ -258,9 +258,9 @@ func convertNewParamsToStreamConfig(
cfg.ReadSelectors[i] = readSelectors[i].Clone()
}
- for _, f := range opts {
- if f != nil {
- f(&cfg)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&cfg)
}
}
diff --git a/internal/topic/topicreaderinternal/stream_reader_impl.go b/internal/topic/topicreaderinternal/stream_reader_impl.go
index 92c28f19f..fec186ffc 100644
--- a/internal/topic/topicreaderinternal/stream_reader_impl.go
+++ b/internal/topic/topicreaderinternal/stream_reader_impl.go
@@ -27,8 +27,7 @@ const defaultBufferSize = 1024 * 1024
var (
PublicErrCommitSessionToExpiredSession = xerrors.Wrap(errors.New("ydb: commit to expired session"))
- errPartitionSessionStoppedByServer = xerrors.Wrap(errors.New("ydb: topic partition session stopped by server"))
- errCommitWithNilPartitionSession = xerrors.Wrap(errors.New("ydb: commit with nil partition session"))
+ errCommitWithNilPartitionSession = xerrors.Wrap(errors.New("ydb: commit with nil partition session"))
)
type partitionSessionID = rawtopicreader.PartitionSessionID
@@ -387,7 +386,7 @@ func (r *topicStreamReaderImpl) checkCommitRange(commitRange commitRange) error
}
if session.Context().Err() != nil {
- return xerrors.WithStackTrace(fmt.Errorf("ydb: commit error: %w", errPartitionSessionStoppedByServer))
+ return xerrors.WithStackTrace(PublicErrCommitSessionToExpiredSession)
}
ownSession, err := r.sessionController.Get(session.partitionSessionID)
diff --git a/internal/topic/topicreaderinternal/stream_reconnector.go b/internal/topic/topicreaderinternal/stream_reconnector.go
index fa3ba6594..ec601ba67 100644
--- a/internal/topic/topicreaderinternal/stream_reconnector.go
+++ b/internal/topic/topicreaderinternal/stream_reconnector.go
@@ -29,30 +29,24 @@ var (
type readerConnectFunc func(ctx context.Context) (batchedStreamReader, error)
type readerReconnector struct {
- clock clockwork.Clock
- background background.Worker
-
- tracer *trace.Topic
- baseContext context.Context
- retrySettings topic.RetrySettings
-
- readerConnect readerConnectFunc
-
- reconnectFromBadStream chan reconnectRequest
- connectTimeout time.Duration
-
- closeOnce sync.Once
- readerID int64
-
- m xsync.RWMutex
- streamConnectionInProgress empty.Chan // opened if connection in progress, closed if connection established
+ background background.Worker
+ clock clockwork.Clock
+ baseContext context.Context
+ retrySettings topic.RetrySettings
streamVal batchedStreamReader
streamErr error
closedErr error
-
- initErr error
- initDone bool
- initDoneCh empty.Chan
+ initErr error
+ tracer *trace.Topic
+ readerConnect readerConnectFunc
+ reconnectFromBadStream chan reconnectRequest
+ connectTimeout time.Duration
+ readerID int64
+ streamConnectionInProgress empty.Chan // opened if connection in progress, closed if connection established
+ initDoneCh empty.Chan
+ m xsync.RWMutex
+ closeOnce sync.Once
+ initDone bool
}
//nolint:revive
@@ -334,9 +328,12 @@ func (r *readerReconnector) connectWithTimeout() (_ batchedStreamReader, err err
result <- connectResult{stream: stream, err: err}
}()
+ connectionTimoutTimer := r.clock.NewTimer(r.connectTimeout)
+ defer connectionTimoutTimer.Stop()
+
var res connectResult
select {
- case <-r.clock.After(r.connectTimeout):
+ case <-connectionTimoutTimer.Chan():
// cancel connection context only if timeout exceed while connection
// because if cancel context after connect - it will break
cancel()
diff --git a/internal/topic/topicwriterinternal/message.go b/internal/topic/topicwriterinternal/message.go
index 1e07f2587..5c618e631 100644
--- a/internal/topic/topicwriterinternal/message.go
+++ b/internal/topic/topicwriterinternal/message.go
@@ -58,14 +58,14 @@ type messageWithDataContent struct {
PublicMessage
dataWasRead bool
- encoders *EncoderMap
hasRawContent bool
- rawBuf bytes.Buffer
hasEncodedContent bool
+ metadataCached bool
bufCodec rawtopiccommon.Codec
bufEncoded bytes.Buffer
+ rawBuf bytes.Buffer
+ encoders *EncoderMap
BufUncompressedSize int
- metadataCached bool
}
func (m *messageWithDataContent) GetEncodedBytes(codec rawtopiccommon.Codec) ([]byte, error) {
diff --git a/internal/topic/topicwriterinternal/queue.go b/internal/topic/topicwriterinternal/queue.go
index 338f917b9..799b02d16 100644
--- a/internal/topic/topicwriterinternal/queue.go
+++ b/internal/topic/topicwriterinternal/queue.go
@@ -14,6 +14,7 @@ import (
var (
errCloseClosedMessageQueue = xerrors.Wrap(errors.New("ydb: close closed message queue"))
+ errAckOnClosedMessageQueue = xerrors.Wrap(errors.New("ydb: ack on closed message queue"))
errGetMessageFromClosedQueue = xerrors.Wrap(errors.New("ydb: get message from closed message queue"))
errAddUnorderedMessages = xerrors.Wrap(errors.New("ydb: add unordered messages"))
errAckUnexpectedMessage = xerrors.Wrap(errors.New("ydb: ack unexpected message"))
@@ -152,6 +153,9 @@ func (q *messageQueue) AcksReceived(acks []rawtopicwriter.WriteAck) error {
q.OnAckReceived(ackReceivedCounter)
}
}()
+ if q.closed {
+ return xerrors.WithStackTrace(errAckOnClosedMessageQueue)
+ }
for i := range acks {
if err := q.ackReceivedNeedLock(acks[i].SeqNo); err != nil {
diff --git a/internal/topic/topicwriterinternal/queue_test.go b/internal/topic/topicwriterinternal/queue_test.go
index 8cc6ade34..d62e6b16e 100644
--- a/internal/topic/topicwriterinternal/queue_test.go
+++ b/internal/topic/topicwriterinternal/queue_test.go
@@ -407,6 +407,26 @@ func TestQueuePanicOnOverflow(t *testing.T) {
})
}
+func TestRegressionIssue1038_ReceiveAckAfterCloseQueue(t *testing.T) {
+ counter := 0
+
+ q := newMessageQueue()
+ q.OnAckReceived = func(count int) {
+ counter -= count
+ }
+ require.NoError(t, q.AddMessages(newTestMessagesWithContent(1)))
+ counter++
+
+ require.NoError(t, q.Close(errors.New("test err")))
+ require.ErrorIs(t, q.AcksReceived([]rawtopicwriter.WriteAck{
+ {
+ SeqNo: 1,
+ MessageWriteStatus: rawtopicwriter.MessageWriteStatus{},
+ },
+ }), errAckOnClosedMessageQueue)
+ require.Zero(t, counter)
+}
+
func TestQueue_Ack(t *testing.T) {
t.Run("First", func(t *testing.T) {
q := newMessageQueue()
diff --git a/internal/topic/topicwriterinternal/writer_reconnector.go b/internal/topic/topicwriterinternal/writer_reconnector.go
index 81571248e..5e88be376 100644
--- a/internal/topic/topicwriterinternal/writer_reconnector.go
+++ b/internal/topic/topicwriterinternal/writer_reconnector.go
@@ -119,25 +119,22 @@ func newWriterReconnectorConfig(options ...PublicWriterOption) WriterReconnector
}
type WriterReconnector struct {
- cfg WriterReconnectorConfig
- retrySettings topic.RetrySettings
-
- semaphore *semaphore.Weighted
+ cfg WriterReconnectorConfig
queue messageQueue
background background.Worker
+ retrySettings topic.RetrySettings
clock clockwork.Clock
- firstConnectionHandled atomic.Bool
- firstInitResponseProcessedChan empty.Chan
writerInstanceID string
-
- m xsync.RWMutex
- sessionID string
- lastSeqNo int64
- encodersMap *EncoderMap
-
- initDone bool
- initDoneCh empty.Chan
- initInfo InitialInfo
+ sessionID string
+ semaphore *semaphore.Weighted
+ firstInitResponseProcessedChan empty.Chan
+ lastSeqNo int64
+ encodersMap *EncoderMap
+ initDoneCh empty.Chan
+ initInfo InitialInfo
+ m xsync.RWMutex
+ firstConnectionHandled atomic.Bool
+ initDone bool
}
func newWriterReconnector(
@@ -358,7 +355,7 @@ func (w *WriterReconnector) connectionLoop(ctx context.Context) {
createStreamContext := func() (context.Context, context.CancelFunc) {
// need suppress parent context cancelation for flush buffer while close writer
- return xcontext.WithCancel(xcontext.WithoutDeadline(ctx))
+ return xcontext.WithCancel(xcontext.ValueOnly(ctx))
}
//nolint:ineffassign,staticcheck,wastedassign
@@ -388,16 +385,21 @@ func (w *WriterReconnector) connectionLoop(ctx context.Context) {
prevAttemptTime = now
if reconnectReason != nil {
- if backoff, retry := topic.CheckRetryMode(reconnectReason, w.retrySettings, w.clock.Since(startOfRetries)); retry {
+ retryDuration := w.clock.Since(startOfRetries)
+ if backoff, retry := topic.CheckRetryMode(reconnectReason, w.retrySettings, retryDuration); retry {
delay := backoff.Delay(attempt)
+ delayTimer := w.clock.NewTimer(delay)
select {
case <-doneCtx:
+ delayTimer.Stop()
+
return
- case <-w.clock.After(delay):
+ case <-delayTimer.Chan():
+ delayTimer.Stop() // no really need, stop for common style only
// pass
}
} else {
- _ = w.close(ctx, reconnectReason)
+ _ = w.close(ctx, fmt.Errorf("%w, was retried (%v)", reconnectReason, retryDuration))
return
}
diff --git a/internal/topic/topicwriterinternal/writer_single_stream.go b/internal/topic/topicwriterinternal/writer_single_stream.go
index 444ae3d3d..4f01c56d1 100644
--- a/internal/topic/topicwriterinternal/writer_single_stream.go
+++ b/internal/topic/topicwriterinternal/writer_single_stream.go
@@ -47,19 +47,18 @@ func newSingleStreamWriterConfig(
}
type SingleStreamWriter struct {
- ReceivedLastSeqNum int64
- LastSeqNumRequested bool
+ cfg SingleStreamWriterConfig
+ Encoder EncoderSelector
+ background background.Worker
+ CodecsFromServer rawtopiccommon.SupportedCodecs
+ allowedCodecs rawtopiccommon.SupportedCodecs
SessionID string
+ closeReason error
+ ReceivedLastSeqNum int64
PartitionID int64
- CodecsFromServer rawtopiccommon.SupportedCodecs
- Encoder EncoderSelector
-
- cfg SingleStreamWriterConfig
- allowedCodecs rawtopiccommon.SupportedCodecs
- background background.Worker
- closed atomic.Bool
- closeReason error
- closeCompleted empty.Chan
+ closeCompleted empty.Chan
+ closed atomic.Bool
+ LastSeqNumRequested bool
}
func NewSingleStreamWriter(
@@ -84,7 +83,7 @@ func newSingleStreamWriterStopped(
) *SingleStreamWriter {
return &SingleStreamWriter{
cfg: cfg,
- background: *background.NewWorker(xcontext.WithoutDeadline(ctxForPProfLabelsOnly)),
+ background: *background.NewWorker(xcontext.ValueOnly(ctxForPProfLabelsOnly)),
closeCompleted: make(empty.Chan),
}
}
@@ -189,7 +188,7 @@ func (w *SingleStreamWriter) receiveMessagesLoop(ctx context.Context) {
switch m := mess.(type) {
case *rawtopicwriter.WriteResult:
- if err = w.cfg.queue.AcksReceived(m.Acks); err != nil {
+ if err = w.cfg.queue.AcksReceived(m.Acks); err != nil && !errors.Is(err, errCloseClosedMessageQueue) {
reason := xerrors.WithStackTrace(err)
closeCtx, closeCtxCancel := xcontext.WithCancel(ctx)
closeCtxCancel()
diff --git a/internal/types/types.go b/internal/types/types.go
new file mode 100644
index 000000000..4942cd787
--- /dev/null
+++ b/internal/types/types.go
@@ -0,0 +1,948 @@
+package types
+
+import (
+ "fmt"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
+)
+
+type Type interface {
+ Yql() string
+ String() string
+
+ ToYDB(a *allocator.Allocator) *Ydb.Type
+ equalsTo(rhs Type) bool
+}
+
+func TypeToYDB(t Type, a *allocator.Allocator) *Ydb.Type {
+ return t.ToYDB(a)
+}
+
+func TypeFromYDB(x *Ydb.Type) Type {
+ switch v := x.GetType().(type) {
+ case *Ydb.Type_TypeId:
+ return primitiveTypeFromYDB(v.TypeId)
+
+ case *Ydb.Type_OptionalType:
+ return NewOptional(TypeFromYDB(v.OptionalType.GetItem()))
+
+ case *Ydb.Type_ListType:
+ return NewList(TypeFromYDB(v.ListType.GetItem()))
+
+ case *Ydb.Type_DecimalType:
+ d := v.DecimalType
+
+ return NewDecimal(d.GetPrecision(), d.GetScale())
+
+ case *Ydb.Type_TupleType:
+ t := v.TupleType
+
+ return NewTuple(FromYDB(t.GetElements())...)
+
+ case *Ydb.Type_StructType:
+ s := v.StructType
+
+ return NewStruct(StructFields(s.GetMembers())...)
+
+ case *Ydb.Type_DictType:
+ keyType, valueType := TypeFromYDB(v.DictType.GetKey()), TypeFromYDB(v.DictType.GetPayload())
+ if valueType.equalsTo(NewVoid()) {
+ return NewSet(keyType)
+ }
+
+ return NewDict(keyType, valueType)
+
+ case *Ydb.Type_VariantType:
+ t := v.VariantType
+ switch x := t.GetType().(type) {
+ case *Ydb.VariantType_TupleItems:
+ return NewVariantTuple(FromYDB(x.TupleItems.GetElements())...)
+ case *Ydb.VariantType_StructItems:
+ return NewVariantStruct(StructFields(x.StructItems.GetMembers())...)
+ default:
+ panic("ydb: unknown variant type")
+ }
+
+ case *Ydb.Type_VoidType:
+ return NewVoid()
+
+ case *Ydb.Type_NullType:
+ return NewNull()
+
+ case *Ydb.Type_PgType:
+ return &PgType{
+ OID: x.GetPgType().GetOid(),
+ }
+
+ default:
+ panic("ydb: unknown type")
+ }
+}
+
+func primitiveTypeFromYDB(t Ydb.Type_PrimitiveTypeId) Type {
+ switch t {
+ case Ydb.Type_BOOL:
+ return Bool
+ case Ydb.Type_INT8:
+ return Int8
+ case Ydb.Type_UINT8:
+ return Uint8
+ case Ydb.Type_INT16:
+ return Int16
+ case Ydb.Type_UINT16:
+ return Uint16
+ case Ydb.Type_INT32:
+ return Int32
+ case Ydb.Type_UINT32:
+ return Uint32
+ case Ydb.Type_INT64:
+ return Int64
+ case Ydb.Type_UINT64:
+ return Uint64
+ case Ydb.Type_FLOAT:
+ return Float
+ case Ydb.Type_DOUBLE:
+ return Double
+ case Ydb.Type_DATE:
+ return Date
+ case Ydb.Type_DATETIME:
+ return Datetime
+ case Ydb.Type_TIMESTAMP:
+ return Timestamp
+ case Ydb.Type_INTERVAL:
+ return Interval
+ case Ydb.Type_TZ_DATE:
+ return TzDate
+ case Ydb.Type_TZ_DATETIME:
+ return TzDatetime
+ case Ydb.Type_TZ_TIMESTAMP:
+ return TzTimestamp
+ case Ydb.Type_STRING:
+ return Bytes
+ case Ydb.Type_UTF8:
+ return Text
+ case Ydb.Type_YSON:
+ return YSON
+ case Ydb.Type_JSON:
+ return JSON
+ case Ydb.Type_UUID:
+ return UUID
+ case Ydb.Type_JSON_DOCUMENT:
+ return JSONDocument
+ case Ydb.Type_DYNUMBER:
+ return DyNumber
+ default:
+ panic("ydb: unexpected type")
+ }
+}
+
+func FromYDB(es []*Ydb.Type) []Type {
+ ts := make([]Type, len(es))
+ for i, el := range es {
+ ts[i] = TypeFromYDB(el)
+ }
+
+ return ts
+}
+
+func Equal(a, b Type) bool {
+ return a.equalsTo(b)
+}
+
+type Decimal struct {
+ precision uint32
+ scale uint32
+}
+
+func (v *Decimal) Precision() uint32 {
+ return v.precision
+}
+
+func (v *Decimal) Scale() uint32 {
+ return v.scale
+}
+
+func (v *Decimal) String() string {
+ return v.Yql()
+}
+
+func (v *Decimal) Name() string {
+ return "Decimal"
+}
+
+func (v *Decimal) Yql() string {
+ return fmt.Sprintf("%s(%d,%d)", v.Name(), v.precision, v.scale)
+}
+
+func (v *Decimal) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(*Decimal)
+
+ return ok && *v == *vv
+}
+
+func (v *Decimal) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ decimal := a.Decimal()
+
+ decimal.Scale = v.scale
+ decimal.Precision = v.precision
+
+ typeDecimal := a.TypeDecimal()
+ typeDecimal.DecimalType = decimal
+
+ t := a.Type()
+ t.Type = typeDecimal
+
+ return t
+}
+
+func NewDecimal(precision, scale uint32) *Decimal {
+ return &Decimal{
+ precision: precision,
+ scale: scale,
+ }
+}
+
+type Dict struct {
+ keyType Type
+ valueType Type
+}
+
+func (v *Dict) KeyType() Type {
+ return v.keyType
+}
+
+func (v *Dict) ValueType() Type {
+ return v.valueType
+}
+
+func (v *Dict) String() string {
+ return v.Yql()
+}
+
+func (v *Dict) Yql() string {
+ buffer := xstring.Buffer()
+ defer buffer.Free()
+ buffer.WriteString("Dict<")
+ buffer.WriteString(v.keyType.Yql())
+ buffer.WriteByte(',')
+ buffer.WriteString(v.valueType.Yql())
+ buffer.WriteByte('>')
+
+ return buffer.String()
+}
+
+func (v *Dict) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(*Dict)
+ if !ok {
+ return false
+ }
+ if !v.keyType.equalsTo(vv.keyType) {
+ return false
+ }
+ if !v.valueType.equalsTo(vv.valueType) {
+ return false
+ }
+
+ return true
+}
+
+func (v *Dict) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ typeDict := a.TypeDict()
+
+ typeDict.DictType = a.Dict()
+
+ typeDict.DictType.Key = v.keyType.ToYDB(a)
+ typeDict.DictType.Payload = v.valueType.ToYDB(a)
+
+ t.Type = typeDict
+
+ return t
+}
+
+func NewDict(key, value Type) (v *Dict) {
+ return &Dict{
+ keyType: key,
+ valueType: value,
+ }
+}
+
+type EmptyList struct{}
+
+func (v EmptyList) Yql() string {
+ return "EmptyList"
+}
+
+func (v EmptyList) String() string {
+ return v.Yql()
+}
+
+func (EmptyList) equalsTo(rhs Type) bool {
+ _, ok := rhs.(EmptyList)
+
+ return ok
+}
+
+func (EmptyList) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ t.Type = a.TypeEmptyList()
+
+ return t
+}
+
+func NewEmptyList() EmptyList {
+ return EmptyList{}
+}
+
+type EmptyDict struct{}
+
+func (v EmptyDict) String() string {
+ return v.Yql()
+}
+
+func (v EmptyDict) Yql() string {
+ return "EmptyDict"
+}
+
+func (EmptyDict) equalsTo(rhs Type) bool {
+ _, ok := rhs.(EmptyDict)
+
+ return ok
+}
+
+func (EmptyDict) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ t.Type = a.TypeEmptyDict()
+
+ return t
+}
+
+func EmptySet() EmptyDict {
+ return EmptyDict{}
+}
+
+func NewEmptyDict() EmptyDict {
+ return EmptyDict{}
+}
+
+type List struct {
+ itemType Type
+}
+
+func (v *List) ItemType() Type {
+ return v.itemType
+}
+
+func (v *List) String() string {
+ return v.Yql()
+}
+
+func (v *List) Yql() string {
+ return "List<" + v.itemType.Yql() + ">"
+}
+
+func (v *List) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(*List)
+ if !ok {
+ return false
+ }
+
+ return v.itemType.equalsTo(vv.itemType)
+}
+
+func (v *List) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ list := a.List()
+
+ list.Item = v.itemType.ToYDB(a)
+
+ typeList := a.TypeList()
+ typeList.ListType = list
+
+ t.Type = typeList
+
+ return t
+}
+
+func NewList(t Type) *List {
+ return &List{
+ itemType: t,
+ }
+}
+
+type Set struct {
+ itemType Type
+}
+
+func (v *Set) ItemType() Type {
+ return v.itemType
+}
+
+func (v *Set) String() string {
+ return v.Yql()
+}
+
+func (v *Set) Yql() string {
+ return "Set<" + v.itemType.Yql() + ">"
+}
+
+func (v *Set) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(*Set)
+ if !ok {
+ return false
+ }
+
+ return v.itemType.equalsTo(vv.itemType)
+}
+
+func (v *Set) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ typeDict := a.TypeDict()
+
+ typeDict.DictType = a.Dict()
+
+ typeDict.DictType.Key = v.itemType.ToYDB(a)
+ typeDict.DictType.Payload = _voidType
+
+ t.Type = typeDict
+
+ return t
+}
+
+func NewSet(t Type) *Set {
+ return &Set{
+ itemType: t,
+ }
+}
+
+type Optional struct {
+ innerType Type
+}
+
+func (v Optional) IsOptional() {}
+
+func (v Optional) InnerType() Type {
+ return v.innerType
+}
+
+func (v Optional) String() string {
+ return v.Yql()
+}
+
+func (v Optional) Yql() string {
+ return "Optional<" + v.innerType.Yql() + ">"
+}
+
+func (v Optional) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(Optional)
+ if !ok {
+ return false
+ }
+
+ return v.innerType.equalsTo(vv.innerType)
+}
+
+func (v Optional) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ typeOptional := a.TypeOptional()
+
+ typeOptional.OptionalType = a.Optional()
+
+ typeOptional.OptionalType.Item = v.innerType.ToYDB(a)
+
+ t.Type = typeOptional
+
+ return t
+}
+
+func NewOptional(t Type) Optional {
+ return Optional{
+ innerType: t,
+ }
+}
+
+type PgType struct {
+ OID uint32
+}
+
+func (v PgType) String() string {
+ return v.Yql()
+}
+
+func (v PgType) Yql() string {
+ return fmt.Sprintf("PgType(%v)", v.OID)
+}
+
+func (v PgType) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ //nolint:godox
+ // TODO: make allocator
+ return &Ydb.Type{Type: &Ydb.Type_PgType{
+ PgType: &Ydb.PgType{
+ Oid: v.OID,
+ },
+ }}
+}
+
+func (v PgType) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(PgType)
+ if !ok {
+ return false
+ }
+
+ return v.OID == vv.OID
+}
+
+type Primitive uint
+
+func (v Primitive) String() string {
+ return v.Yql()
+}
+
+func (v Primitive) Yql() string {
+ return primitiveString[v]
+}
+
+const (
+ Unknown Primitive = iota
+ Bool
+ Int8
+ Uint8
+ Int16
+ Uint16
+ Int32
+ Uint32
+ Int64
+ Uint64
+ Float
+ Double
+ Date
+ Datetime
+ Timestamp
+ Interval
+ TzDate
+ TzDatetime
+ TzTimestamp
+ Bytes
+ Text
+ YSON
+ JSON
+ UUID
+ JSONDocument
+ DyNumber
+)
+
+var primitive = [...]*Ydb.Type{
+ Bool: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL}},
+ Int8: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8}},
+ Uint8: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8}},
+ Int16: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16}},
+ Uint16: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16}},
+ Int32: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32}},
+ Uint32: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32}},
+ Int64: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64}},
+ Uint64: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64}},
+ Float: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT}},
+ Double: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE}},
+ Date: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE}},
+ Datetime: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME}},
+ Timestamp: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP}},
+ Interval: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL}},
+ TzDate: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE}},
+ TzDatetime: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME}},
+ TzTimestamp: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP}},
+ Bytes: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING}},
+ Text: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8}},
+ YSON: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON}},
+ JSON: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON}},
+ UUID: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID}},
+ JSONDocument: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT}},
+ DyNumber: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DYNUMBER}},
+}
+
+var primitiveString = [...]string{
+ Unknown: "",
+ Bool: "Bool",
+ Int8: "Int8",
+ Uint8: "Uint8",
+ Int16: "Int16",
+ Uint16: "Uint16",
+ Int32: "Int32",
+ Uint32: "Uint32",
+ Int64: "Int64",
+ Uint64: "Uint64",
+ Float: "Float",
+ Double: "Double",
+ Date: "Date",
+ Datetime: "Datetime",
+ Timestamp: "Timestamp",
+ Interval: "Interval",
+ TzDate: "TzDate",
+ TzDatetime: "TzDatetime",
+ TzTimestamp: "TzTimestamp",
+ Bytes: "String",
+ Text: "Utf8",
+ YSON: "Yson",
+ JSON: "Json",
+ UUID: "Uuid",
+ JSONDocument: "JsonDocument",
+ DyNumber: "DyNumber",
+}
+
+func (v Primitive) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(Primitive)
+ if !ok {
+ return false
+ }
+
+ return v == vv
+}
+
+func (v Primitive) ToYDB(*allocator.Allocator) *Ydb.Type {
+ return primitive[v]
+}
+
+type (
+ StructField struct {
+ Name string
+ T Type
+ }
+ Struct struct {
+ fields []StructField
+ }
+)
+
+func (v *Struct) Field(i int) StructField {
+ return v.fields[i]
+}
+
+func (v *Struct) Fields() []StructField {
+ return v.fields
+}
+
+func (v *Struct) String() string {
+ return v.Yql()
+}
+
+func (v *Struct) Yql() string {
+ buffer := xstring.Buffer()
+ defer buffer.Free()
+ buffer.WriteString("Struct<")
+ for i := range v.fields {
+ if i > 0 {
+ buffer.WriteByte(',')
+ }
+ buffer.WriteString("'" + v.fields[i].Name + "'")
+ buffer.WriteByte(':')
+ buffer.WriteString(v.fields[i].T.Yql())
+ }
+ buffer.WriteByte('>')
+
+ return buffer.String()
+}
+
+func (v *Struct) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(*Struct)
+ if !ok {
+ return false
+ }
+ if len(v.fields) != len(vv.fields) {
+ return false
+ }
+ for i := range v.fields {
+ if v.fields[i].Name != vv.fields[i].Name {
+ return false
+ }
+ if !v.fields[i].T.equalsTo(vv.fields[i].T) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (v *Struct) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ typeStruct := a.TypeStruct()
+
+ typeStruct.StructType = a.Struct()
+
+ for i := range v.fields {
+ structMember := a.StructMember()
+ structMember.Name = v.fields[i].Name
+ structMember.Type = v.fields[i].T.ToYDB(a)
+ typeStruct.StructType.Members = append(
+ typeStruct.StructType.GetMembers(),
+ structMember,
+ )
+ }
+
+ t.Type = typeStruct
+
+ return t
+}
+
+func NewStruct(fields ...StructField) (v *Struct) {
+ return &Struct{
+ fields: fields,
+ }
+}
+
+func StructFields(ms []*Ydb.StructMember) []StructField {
+ fs := make([]StructField, len(ms))
+ for i, m := range ms {
+ fs[i] = StructField{
+ Name: m.GetName(),
+ T: TypeFromYDB(m.GetType()),
+ }
+ }
+
+ return fs
+}
+
+type Tuple struct {
+ innerTypes []Type
+}
+
+func (v *Tuple) InnerTypes() []Type {
+ return v.innerTypes
+}
+
+func (v *Tuple) ItemType(i int) Type {
+ return v.innerTypes[i]
+}
+
+func (v *Tuple) String() string {
+ return v.Yql()
+}
+
+func (v *Tuple) Yql() string {
+ buffer := xstring.Buffer()
+ defer buffer.Free()
+ buffer.WriteString("Tuple<")
+ for i, t := range v.innerTypes {
+ if i > 0 {
+ buffer.WriteByte(',')
+ }
+ buffer.WriteString(t.Yql())
+ }
+ buffer.WriteByte('>')
+
+ return buffer.String()
+}
+
+func (v *Tuple) equalsTo(rhs Type) bool {
+ vv, ok := rhs.(*Tuple)
+ if !ok {
+ return false
+ }
+ if len(v.innerTypes) != len(vv.innerTypes) {
+ return false
+ }
+ for i := range v.innerTypes {
+ if !v.innerTypes[i].equalsTo(vv.innerTypes[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (v *Tuple) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ var items []Type
+ if v != nil {
+ items = v.innerTypes
+ }
+ t := a.Type()
+
+ typeTuple := a.TypeTuple()
+
+ typeTuple.TupleType = a.Tuple()
+
+ for _, vv := range items {
+ typeTuple.TupleType.Elements = append(typeTuple.TupleType.GetElements(), vv.ToYDB(a))
+ }
+
+ t.Type = typeTuple
+
+ return t
+}
+
+func NewTuple(items ...Type) (v *Tuple) {
+ return &Tuple{
+ innerTypes: items,
+ }
+}
+
+type VariantStruct struct {
+ *Struct
+}
+
+func (v *VariantStruct) Yql() string {
+ buffer := xstring.Buffer()
+ defer buffer.Free()
+ buffer.WriteString("Variant<")
+ for i := range v.fields {
+ if i > 0 {
+ buffer.WriteByte(',')
+ }
+ buffer.WriteString("'" + v.fields[i].Name + "'")
+ buffer.WriteByte(':')
+ buffer.WriteString(v.fields[i].T.Yql())
+ }
+ buffer.WriteByte('>')
+
+ return buffer.String()
+}
+
+func (v *VariantStruct) equalsTo(rhs Type) bool {
+ switch t := rhs.(type) {
+ case *VariantStruct:
+ return v.Struct.equalsTo(t.Struct)
+ case *Struct:
+ return v.Struct.equalsTo(t)
+ default:
+ return false
+ }
+}
+
+func (v *VariantStruct) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ typeVariant := a.TypeVariant()
+
+ typeVariant.VariantType = a.Variant()
+
+ structItems := a.VariantStructItems()
+ structItems.StructItems = v.Struct.ToYDB(a).GetType().(*Ydb.Type_StructType).StructType
+
+ typeVariant.VariantType.Type = structItems
+
+ t.Type = typeVariant
+
+ return t
+}
+
+func NewVariantStruct(fields ...StructField) *VariantStruct {
+ return &VariantStruct{
+ Struct: NewStruct(fields...),
+ }
+}
+
+type VariantTuple struct {
+ *Tuple
+}
+
+func (v *VariantTuple) Yql() string {
+ buffer := xstring.Buffer()
+ defer buffer.Free()
+ buffer.WriteString("Variant<")
+ for i, t := range v.innerTypes {
+ if i > 0 {
+ buffer.WriteByte(',')
+ }
+ buffer.WriteString(t.Yql())
+ }
+ buffer.WriteByte('>')
+
+ return buffer.String()
+}
+
+func (v *VariantTuple) equalsTo(rhs Type) bool {
+ switch t := rhs.(type) {
+ case *VariantTuple:
+ return v.Tuple.equalsTo(t.Tuple)
+ case *Tuple:
+ return v.Tuple.equalsTo(t)
+ default:
+ return false
+ }
+}
+
+func (v *VariantTuple) ToYDB(a *allocator.Allocator) *Ydb.Type {
+ t := a.Type()
+
+ typeVariant := a.TypeVariant()
+
+ typeVariant.VariantType = a.Variant()
+
+ tupleItems := a.VariantTupleItems()
+ tupleItems.TupleItems = v.Tuple.ToYDB(a).GetType().(*Ydb.Type_TupleType).TupleType
+
+ typeVariant.VariantType.Type = tupleItems
+
+ t.Type = typeVariant
+
+ return t
+}
+
+func NewVariantTuple(items ...Type) *VariantTuple {
+ return &VariantTuple{
+ Tuple: NewTuple(items...),
+ }
+}
+
+type Void struct{}
+
+func (v Void) String() string {
+ return v.Yql()
+}
+
+func (v Void) Yql() string {
+ return "Void"
+}
+
+var _voidType = &Ydb.Type{
+ Type: &Ydb.Type_VoidType{},
+}
+
+func (v Void) equalsTo(rhs Type) bool {
+ _, ok := rhs.(Void)
+
+ return ok
+}
+
+func (Void) ToYDB(*allocator.Allocator) *Ydb.Type {
+ return _voidType
+}
+
+func NewVoid() Void {
+ return Void{}
+}
+
+type Null struct{}
+
+func (v Null) String() string {
+ return v.Yql()
+}
+
+func (v Null) Yql() string {
+ return "Null"
+}
+
+var _nullType = &Ydb.Type{
+ Type: &Ydb.Type_NullType{},
+}
+
+func (v Null) equalsTo(rhs Type) bool {
+ _, ok := rhs.(Null)
+
+ return ok
+}
+
+func (Null) ToYDB(*allocator.Allocator) *Ydb.Type {
+ return _nullType
+}
+
+func NewNull() Null {
+ return Null{}
+}
diff --git a/internal/types/types_test.go b/internal/types/types_test.go
new file mode 100644
index 000000000..3c9936a2a
--- /dev/null
+++ b/internal/types/types_test.go
@@ -0,0 +1,381 @@
+package types
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pg"
+)
+
+func TestTypeToString(t *testing.T) {
+ for _, tt := range []struct {
+ t Type
+ s string
+ }{
+ {
+ t: NewVoid(),
+ s: "Void",
+ },
+ {
+ t: NewNull(),
+ s: "Null",
+ },
+ {
+ t: Bool,
+ s: "Bool",
+ },
+ {
+ t: Int8,
+ s: "Int8",
+ },
+ {
+ t: Uint8,
+ s: "Uint8",
+ },
+ {
+ t: Int16,
+ s: "Int16",
+ },
+ {
+ t: Uint16,
+ s: "Uint16",
+ },
+ {
+ t: Int32,
+ s: "Int32",
+ },
+ {
+ t: Uint32,
+ s: "Uint32",
+ },
+ {
+ t: Int64,
+ s: "Int64",
+ },
+ {
+ t: Uint64,
+ s: "Uint64",
+ },
+ {
+ t: Float,
+ s: "Float",
+ },
+ {
+ t: Double,
+ s: "Double",
+ },
+ {
+ t: Date,
+ s: "Date",
+ },
+ {
+ t: Datetime,
+ s: "Datetime",
+ },
+ {
+ t: Timestamp,
+ s: "Timestamp",
+ },
+ {
+ t: Interval,
+ s: "Interval",
+ },
+ {
+ t: TzDate,
+ s: "TzDate",
+ },
+ {
+ t: TzDatetime,
+ s: "TzDatetime",
+ },
+ {
+ t: TzTimestamp,
+ s: "TzTimestamp",
+ },
+ {
+ t: Bytes,
+ s: "String",
+ },
+ {
+ t: Text,
+ s: "Utf8",
+ },
+ {
+ t: YSON,
+ s: "Yson",
+ },
+ {
+ t: JSON,
+ s: "Json",
+ },
+ {
+ t: UUID,
+ s: "Uuid",
+ },
+ {
+ t: JSONDocument,
+ s: "JsonDocument",
+ },
+ {
+ t: DyNumber,
+ s: "DyNumber",
+ },
+ {
+ t: NewOptional(Bool),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Int8),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Uint8),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Int16),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Uint16),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Int32),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Uint32),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Int64),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Uint64),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Float),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Double),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Date),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Datetime),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Timestamp),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Interval),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(TzDate),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(TzDatetime),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(TzTimestamp),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Bytes),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(Text),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(YSON),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(JSON),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(UUID),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(JSONDocument),
+ s: "Optional",
+ },
+ {
+ t: NewOptional(DyNumber),
+ s: "Optional",
+ },
+ {
+ t: NewDecimal(22, 9),
+ s: "Decimal(22,9)",
+ },
+ {
+ t: NewDict(Text, Timestamp),
+ s: "Dict",
+ },
+ {
+ t: NewEmptyList(),
+ s: "EmptyList",
+ },
+ {
+ t: NewList(Uint32),
+ s: "List",
+ },
+ {
+ t: NewSet(Uint32),
+ s: "Set",
+ },
+ {
+ t: EmptySet(),
+ s: "EmptyDict",
+ },
+ {
+ t: NewEmptyDict(),
+ s: "EmptyDict",
+ },
+ {
+ t: NewVariantStruct(
+ StructField{
+ Name: "a",
+ T: Bool,
+ },
+ StructField{
+ Name: "b",
+ T: Float,
+ },
+ ),
+ s: "Variant<'a':Bool,'b':Float>",
+ },
+ {
+ t: NewVariantTuple(
+ Bool,
+ Float,
+ ),
+ s: "Variant",
+ },
+ {
+ t: PgType{OID: pg.OIDUnknown},
+ s: "PgType(705)",
+ },
+ } {
+ t.Run(tt.s, func(t *testing.T) {
+ if got := tt.t.Yql(); got != tt.s {
+ t.Errorf("s representations not equals:\n\n - got: %s\n\n - want: %s", got, tt.s)
+ }
+ })
+ }
+}
+
+func TestEqual(t *testing.T) {
+ tests := []struct {
+ lhs Type
+ rhs Type
+ equal bool
+ }{
+ {
+ Bool,
+ Bool,
+ true,
+ },
+ {
+ Bool,
+ Text,
+ false,
+ },
+ {
+ Text,
+ Text,
+ true,
+ },
+ {
+ NewOptional(Bool),
+ NewOptional(Bool),
+ true,
+ },
+ {
+ NewOptional(Bool),
+ NewOptional(Text),
+ false,
+ },
+ {
+ NewOptional(Text),
+ NewOptional(Text),
+ true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run("", func(t *testing.T) {
+ if equal := Equal(tt.lhs, tt.rhs); equal != tt.equal {
+ t.Errorf("Equal(%s, %s) = %v, want %v", tt.lhs, tt.rhs, equal, tt.equal)
+ }
+ })
+ }
+}
+
+func TestOptionalInnerType(t *testing.T) {
+ tests := []struct {
+ src Type
+ innerType Type
+ isOptional bool
+ }{
+ {
+ Bool,
+ nil,
+ false,
+ },
+ {
+ Text,
+ nil,
+ false,
+ },
+ {
+ NewOptional(Bool),
+ Bool,
+ true,
+ },
+ {
+ NewOptional(Text),
+ Text,
+ true,
+ },
+ {
+ NewOptional(NewTuple(Text, Bool, Uint64, NewOptional(Int64))),
+ NewTuple(Text, Bool, Uint64, NewOptional(Int64)),
+ true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run("", func(t *testing.T) {
+ optional, isOptional := tt.src.(interface {
+ IsOptional()
+ InnerType() Type
+ })
+ require.Equal(t, tt.isOptional, isOptional)
+ var innerType Type
+ if isOptional {
+ innerType = optional.InnerType()
+ }
+ if tt.innerType == nil {
+ require.Nil(t, innerType)
+ } else {
+ require.True(t, Equal(tt.innerType, innerType))
+ }
+ })
+ }
+}
diff --git a/internal/value/errors.go b/internal/value/errors.go
new file mode 100644
index 000000000..0c3212bbf
--- /dev/null
+++ b/internal/value/errors.go
@@ -0,0 +1,8 @@
+package value
+
+import "errors"
+
+var (
+ ErrCannotCast = errors.New("cannot cast")
+ errDestinationTypeIsNotAPointer = errors.New("destination type is not a pointer")
+)
diff --git a/internal/value/nullable.go b/internal/value/nullable.go
new file mode 100644
index 000000000..1553d3eb1
--- /dev/null
+++ b/internal/value/nullable.go
@@ -0,0 +1,450 @@
+package value
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
+)
+
+func NullableBoolValue(v *bool) Value {
+ if v == nil {
+ return NullValue(types.Bool)
+ }
+
+ return OptionalValue(BoolValue(*v))
+}
+
+func NullableInt8Value(v *int8) Value {
+ if v == nil {
+ return NullValue(types.Int8)
+ }
+
+ return OptionalValue(Int8Value(*v))
+}
+
+func NullableInt16Value(v *int16) Value {
+ if v == nil {
+ return NullValue(types.Int16)
+ }
+
+ return OptionalValue(Int16Value(*v))
+}
+
+func NullableInt32Value(v *int32) Value {
+ if v == nil {
+ return NullValue(types.Int32)
+ }
+
+ return OptionalValue(Int32Value(*v))
+}
+
+func NullableInt64Value(v *int64) Value {
+ if v == nil {
+ return NullValue(types.Int64)
+ }
+
+ return OptionalValue(Int64Value(*v))
+}
+
+func NullableUint8Value(v *uint8) Value {
+ if v == nil {
+ return NullValue(types.Uint8)
+ }
+
+ return OptionalValue(Uint8Value(*v))
+}
+
+func NullableUint16Value(v *uint16) Value {
+ if v == nil {
+ return NullValue(types.Uint16)
+ }
+
+ return OptionalValue(Uint16Value(*v))
+}
+
+func NullableUint32Value(v *uint32) Value {
+ if v == nil {
+ return NullValue(types.Uint32)
+ }
+
+ return OptionalValue(Uint32Value(*v))
+}
+
+func NullableUint64Value(v *uint64) Value {
+ if v == nil {
+ return NullValue(types.Uint64)
+ }
+
+ return OptionalValue(Uint64Value(*v))
+}
+
+func NullableFloatValue(v *float32) Value {
+ if v == nil {
+ return NullValue(types.Float)
+ }
+
+ return OptionalValue(FloatValue(*v))
+}
+
+func NullableDoubleValue(v *float64) Value {
+ if v == nil {
+ return NullValue(types.Double)
+ }
+
+ return OptionalValue(DoubleValue(*v))
+}
+
+func NullableDateValue(v *uint32) Value {
+ if v == nil {
+ return NullValue(types.Date)
+ }
+
+ return OptionalValue(DateValue(*v))
+}
+
+func NullableDateValueFromTime(v *time.Time) Value {
+ if v == nil {
+ return NullValue(types.Date)
+ }
+
+ return OptionalValue(DateValueFromTime(*v))
+}
+
+func NullableDatetimeValue(v *uint32) Value {
+ if v == nil {
+ return NullValue(types.Datetime)
+ }
+
+ return OptionalValue(DatetimeValue(*v))
+}
+
+func NullableDatetimeValueFromTime(v *time.Time) Value {
+ if v == nil {
+ return NullValue(types.Datetime)
+ }
+
+ return OptionalValue(DatetimeValueFromTime(*v))
+}
+
+func NullableTzDateValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.TzDate)
+ }
+
+ return OptionalValue(TzDateValue(*v))
+}
+
+func NullableTzDateValueFromTime(v *time.Time) Value {
+ if v == nil {
+ return NullValue(types.TzDate)
+ }
+
+ return OptionalValue(TzDateValueFromTime(*v))
+}
+
+func NullableTzDatetimeValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.TzDatetime)
+ }
+
+ return OptionalValue(TzDatetimeValue(*v))
+}
+
+func NullableTzDatetimeValueFromTime(v *time.Time) Value {
+ if v == nil {
+ return NullValue(types.TzDatetime)
+ }
+
+ return OptionalValue(TzDatetimeValueFromTime(*v))
+}
+
+func NullableTimestampValue(v *uint64) Value {
+ if v == nil {
+ return NullValue(types.Timestamp)
+ }
+
+ return OptionalValue(TimestampValue(*v))
+}
+
+func NullableTimestampValueFromTime(v *time.Time) Value {
+ if v == nil {
+ return NullValue(types.Timestamp)
+ }
+
+ return OptionalValue(TimestampValueFromTime(*v))
+}
+
+func NullableTzTimestampValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.TzTimestamp)
+ }
+
+ return OptionalValue(TzTimestampValue(*v))
+}
+
+func NullableTzTimestampValueFromTime(v *time.Time) Value {
+ if v == nil {
+ return NullValue(types.TzTimestamp)
+ }
+
+ return OptionalValue(TzTimestampValueFromTime(*v))
+}
+
+func NullableIntervalValueFromMicroseconds(v *int64) Value {
+ if v == nil {
+ return NullValue(types.Interval)
+ }
+
+ return OptionalValue(IntervalValue(*v))
+}
+
+func NullableIntervalValueFromDuration(v *time.Duration) Value {
+ if v == nil {
+ return NullValue(types.Interval)
+ }
+
+ return OptionalValue(IntervalValueFromDuration(*v))
+}
+
+func NullableBytesValue(v *[]byte) Value {
+ if v == nil {
+ return NullValue(types.Bytes)
+ }
+
+ return OptionalValue(BytesValue(*v))
+}
+
+func NullableBytesValueFromString(v *string) Value {
+ if v == nil {
+ return NullValue(types.Bytes)
+ }
+
+ return OptionalValue(BytesValue(xstring.ToBytes(*v)))
+}
+
+func NullableTextValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.Text)
+ }
+
+ return OptionalValue(TextValue(*v))
+}
+
+func NullableYSONValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.YSON)
+ }
+
+ return OptionalValue(YSONValue(xstring.ToBytes(*v)))
+}
+
+func NullableYSONValueFromBytes(v *[]byte) Value {
+ if v == nil {
+ return NullValue(types.YSON)
+ }
+
+ return OptionalValue(YSONValue(*v))
+}
+
+func NullableJSONValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.JSON)
+ }
+
+ return OptionalValue(JSONValue(*v))
+}
+
+func NullableJSONValueFromBytes(v *[]byte) Value {
+ if v == nil {
+ return NullValue(types.JSON)
+ }
+
+ return OptionalValue(JSONValue(xstring.FromBytes(*v)))
+}
+
+func NullableUUIDValue(v *[16]byte) Value {
+ if v == nil {
+ return NullValue(types.UUID)
+ }
+
+ return OptionalValue(UUIDValue(*v))
+}
+
+func NullableJSONDocumentValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.JSONDocument)
+ }
+
+ return OptionalValue(JSONDocumentValue(*v))
+}
+
+func NullableJSONDocumentValueFromBytes(v *[]byte) Value {
+ if v == nil {
+ return NullValue(types.JSONDocument)
+ }
+
+ return OptionalValue(JSONDocumentValue(xstring.FromBytes(*v)))
+}
+
+func NullableDyNumberValue(v *string) Value {
+ if v == nil {
+ return NullValue(types.DyNumber)
+ }
+
+ return OptionalValue(DyNumberValue(*v))
+}
+
+// Nullable makes optional value from nullable type
+// Warning: type interface will be replaced in the future with typed parameters pattern from go1.18
+//
+//nolint:gocyclo
+func Nullable(t types.Type, v interface{}) Value {
+ switch t {
+ case types.Bool:
+ return NullableBoolValue(v.(*bool))
+ case types.Int8:
+ return NullableInt8Value(v.(*int8))
+ case types.Uint8:
+ return NullableUint8Value(v.(*uint8))
+ case types.Int16:
+ return NullableInt16Value(v.(*int16))
+ case types.Uint16:
+ return NullableUint16Value(v.(*uint16))
+ case types.Int32:
+ return NullableInt32Value(v.(*int32))
+ case types.Uint32:
+ return NullableUint32Value(v.(*uint32))
+ case types.Int64:
+ return NullableInt64Value(v.(*int64))
+ case types.Uint64:
+ return NullableUint64Value(v.(*uint64))
+ case types.Float:
+ return NullableFloatValue(v.(*float32))
+ case types.Double:
+ return NullableDoubleValue(v.(*float64))
+ case types.Date:
+ switch tt := v.(type) {
+ case *uint32:
+ return NullableDateValue(tt)
+ case *time.Time:
+ return NullableDateValueFromTime(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeDate", tt))
+ }
+ case types.Datetime:
+ switch tt := v.(type) {
+ case *uint32:
+ return NullableDatetimeValue(tt)
+ case *time.Time:
+ return NullableDatetimeValueFromTime(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeDatetime", tt))
+ }
+ case types.Timestamp:
+ switch tt := v.(type) {
+ case *uint64:
+ return NullableTimestampValue(tt)
+ case *time.Time:
+ return NullableTimestampValueFromTime(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeTimestamp", tt))
+ }
+ case types.Interval:
+ switch tt := v.(type) {
+ case *int64:
+ return NullableIntervalValueFromMicroseconds(tt)
+ case *time.Duration:
+ return NullableIntervalValueFromDuration(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeInterval", tt))
+ }
+ case types.TzDate:
+ switch tt := v.(type) {
+ case *string:
+ return NullableTzDateValue(tt)
+ case *time.Time:
+ return NullableTzDateValueFromTime(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeTzDate", tt))
+ }
+ case types.TzDatetime:
+ switch tt := v.(type) {
+ case *string:
+ return NullableTzDatetimeValue(tt)
+ case *time.Time:
+ return NullableTzDatetimeValueFromTime(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeTzDatetime", tt))
+ }
+ case types.TzTimestamp:
+ switch tt := v.(type) {
+ case *string:
+ return NullableTzTimestampValue(tt)
+ case *time.Time:
+ return NullableTzTimestampValueFromTime(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeTzTimestamp", tt))
+ }
+ case types.Bytes:
+ switch tt := v.(type) {
+ case *[]byte:
+ return NullableBytesValue(tt)
+ case *string:
+ return NullableBytesValueFromString(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeBytes", tt))
+ }
+ case types.Text:
+ switch tt := v.(type) {
+ case *string:
+ return NullableTextValue(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeText", tt))
+ }
+ case types.YSON:
+ switch tt := v.(type) {
+ case *string:
+ return NullableYSONValue(tt)
+ case *[]byte:
+ return NullableYSONValueFromBytes(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeYSON", tt))
+ }
+ case types.JSON:
+ switch tt := v.(type) {
+ case *string:
+ return NullableJSONValue(tt)
+ case *[]byte:
+ return NullableJSONValueFromBytes(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeJSON", tt))
+ }
+ case types.UUID:
+ switch tt := v.(type) {
+ case *[16]byte:
+ return NullableUUIDValue(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeUUID", tt))
+ }
+ case types.JSONDocument:
+ switch tt := v.(type) {
+ case *string:
+ return NullableJSONDocumentValue(tt)
+ case *[]byte:
+ return NullableJSONDocumentValueFromBytes(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeJSONDocument", tt))
+ }
+ case types.DyNumber:
+ switch tt := v.(type) {
+ case *string:
+ return NullableDyNumberValue(tt)
+ default:
+ panic(fmt.Sprintf("unsupported type conversion from %T to TypeDyNumber", tt))
+ }
+ default:
+ panic(fmt.Sprintf("unsupported type: %T", t))
+ }
+}
diff --git a/internal/value/type.go b/internal/value/type.go
deleted file mode 100644
index dd2cc3b07..000000000
--- a/internal/value/type.go
+++ /dev/null
@@ -1,872 +0,0 @@
-package value
-
-import (
- "fmt"
-
- "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
-
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
-)
-
-type Type interface {
- Yql() string
- String() string
-
- toYDB(a *allocator.Allocator) *Ydb.Type
- equalsTo(rhs Type) bool
-}
-
-func TypeToYDB(t Type, a *allocator.Allocator) *Ydb.Type {
- return t.toYDB(a)
-}
-
-func TypeFromYDB(x *Ydb.Type) Type {
- switch v := x.Type.(type) {
- case *Ydb.Type_TypeId:
- return primitiveTypeFromYDB(v.TypeId)
-
- case *Ydb.Type_OptionalType:
- return Optional(TypeFromYDB(v.OptionalType.Item))
-
- case *Ydb.Type_ListType:
- return List(TypeFromYDB(v.ListType.Item))
-
- case *Ydb.Type_DecimalType:
- d := v.DecimalType
-
- return Decimal(d.Precision, d.Scale)
-
- case *Ydb.Type_TupleType:
- t := v.TupleType
-
- return Tuple(TypesFromYDB(t.Elements)...)
-
- case *Ydb.Type_StructType:
- s := v.StructType
-
- return Struct(StructFields(s.Members)...)
-
- case *Ydb.Type_DictType:
- keyType, valueType := TypeFromYDB(v.DictType.Key), TypeFromYDB(v.DictType.Payload)
- if valueType.equalsTo(Void()) {
- return Set(keyType)
- }
-
- return Dict(keyType, valueType)
-
- case *Ydb.Type_VariantType:
- t := v.VariantType
- switch x := t.Type.(type) {
- case *Ydb.VariantType_TupleItems:
- return VariantTuple(TypesFromYDB(x.TupleItems.Elements)...)
- case *Ydb.VariantType_StructItems:
- return VariantStruct(StructFields(x.StructItems.Members)...)
- default:
- panic("ydb: unknown variant type")
- }
-
- case *Ydb.Type_VoidType:
- return Void()
-
- case *Ydb.Type_NullType:
- return Null()
-
- default:
- panic("ydb: unknown type")
- }
-}
-
-func primitiveTypeFromYDB(t Ydb.Type_PrimitiveTypeId) Type {
- switch t {
- case Ydb.Type_BOOL:
- return TypeBool
- case Ydb.Type_INT8:
- return TypeInt8
- case Ydb.Type_UINT8:
- return TypeUint8
- case Ydb.Type_INT16:
- return TypeInt16
- case Ydb.Type_UINT16:
- return TypeUint16
- case Ydb.Type_INT32:
- return TypeInt32
- case Ydb.Type_UINT32:
- return TypeUint32
- case Ydb.Type_INT64:
- return TypeInt64
- case Ydb.Type_UINT64:
- return TypeUint64
- case Ydb.Type_FLOAT:
- return TypeFloat
- case Ydb.Type_DOUBLE:
- return TypeDouble
- case Ydb.Type_DATE:
- return TypeDate
- case Ydb.Type_DATETIME:
- return TypeDatetime
- case Ydb.Type_TIMESTAMP:
- return TypeTimestamp
- case Ydb.Type_INTERVAL:
- return TypeInterval
- case Ydb.Type_TZ_DATE:
- return TypeTzDate
- case Ydb.Type_TZ_DATETIME:
- return TypeTzDatetime
- case Ydb.Type_TZ_TIMESTAMP:
- return TypeTzTimestamp
- case Ydb.Type_STRING:
- return TypeBytes
- case Ydb.Type_UTF8:
- return TypeText
- case Ydb.Type_YSON:
- return TypeYSON
- case Ydb.Type_JSON:
- return TypeJSON
- case Ydb.Type_UUID:
- return TypeUUID
- case Ydb.Type_JSON_DOCUMENT:
- return TypeJSONDocument
- case Ydb.Type_DYNUMBER:
- return TypeDyNumber
- default:
- panic("ydb: unexpected type")
- }
-}
-
-func TypesFromYDB(es []*Ydb.Type) []Type {
- ts := make([]Type, len(es))
- for i, el := range es {
- ts[i] = TypeFromYDB(el)
- }
-
- return ts
-}
-
-func TypesEqual(a, b Type) bool {
- return a.equalsTo(b)
-}
-
-type DecimalType struct {
- Precision uint32
- Scale uint32
-}
-
-func (v *DecimalType) String() string {
- return v.Yql()
-}
-
-func (v *DecimalType) Name() string {
- return "Decimal"
-}
-
-func (v *DecimalType) Yql() string {
- return fmt.Sprintf("%s(%d,%d)", v.Name(), v.Precision, v.Scale)
-}
-
-func (v *DecimalType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(*DecimalType)
-
- return ok && *v == *vv
-}
-
-func (v *DecimalType) toYDB(a *allocator.Allocator) *Ydb.Type {
- decimal := a.Decimal()
-
- decimal.Scale = v.Scale
- decimal.Precision = v.Precision
-
- typeDecimal := a.TypeDecimal()
- typeDecimal.DecimalType = decimal
-
- t := a.Type()
- t.Type = typeDecimal
-
- return t
-}
-
-func Decimal(precision, scale uint32) *DecimalType {
- return &DecimalType{
- Precision: precision,
- Scale: scale,
- }
-}
-
-type dictType struct {
- keyType Type
- valueType Type
-}
-
-func (v *dictType) String() string {
- return v.Yql()
-}
-
-func (v *dictType) Yql() string {
- buffer := xstring.Buffer()
- defer buffer.Free()
- buffer.WriteString("Dict<")
- buffer.WriteString(v.keyType.Yql())
- buffer.WriteByte(',')
- buffer.WriteString(v.valueType.Yql())
- buffer.WriteByte('>')
-
- return buffer.String()
-}
-
-func (v *dictType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(*dictType)
- if !ok {
- return false
- }
- if !v.keyType.equalsTo(vv.keyType) {
- return false
- }
- if !v.valueType.equalsTo(vv.valueType) {
- return false
- }
-
- return true
-}
-
-func (v *dictType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- typeDict := a.TypeDict()
-
- typeDict.DictType = a.Dict()
-
- typeDict.DictType.Key = v.keyType.toYDB(a)
- typeDict.DictType.Payload = v.valueType.toYDB(a)
-
- t.Type = typeDict
-
- return t
-}
-
-func Dict(key, value Type) (v *dictType) {
- return &dictType{
- keyType: key,
- valueType: value,
- }
-}
-
-type emptyListType struct{}
-
-func (v emptyListType) Yql() string {
- return "EmptyList"
-}
-
-func (v emptyListType) String() string {
- return v.Yql()
-}
-
-func (emptyListType) equalsTo(rhs Type) bool {
- _, ok := rhs.(emptyListType)
-
- return ok
-}
-
-func (emptyListType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- t.Type = a.TypeEmptyList()
-
- return t
-}
-
-func EmptyList() emptyListType {
- return emptyListType{}
-}
-
-type emptyDictType struct{}
-
-func (v emptyDictType) String() string {
- return v.Yql()
-}
-
-func (v emptyDictType) Yql() string {
- return "EmptyDict"
-}
-
-func (emptyDictType) equalsTo(rhs Type) bool {
- _, ok := rhs.(emptyDictType)
-
- return ok
-}
-
-func (emptyDictType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- t.Type = a.TypeEmptyDict()
-
- return t
-}
-
-func EmptySet() emptyDictType {
- return emptyDictType{}
-}
-
-func EmptyDict() emptyDictType {
- return emptyDictType{}
-}
-
-type listType struct {
- itemType Type
-}
-
-func (v *listType) String() string {
- return v.Yql()
-}
-
-func (v *listType) Yql() string {
- return "List<" + v.itemType.Yql() + ">"
-}
-
-func (v *listType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(*listType)
- if !ok {
- return false
- }
-
- return v.itemType.equalsTo(vv.itemType)
-}
-
-func (v *listType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- list := a.List()
-
- list.Item = v.itemType.toYDB(a)
-
- typeList := a.TypeList()
- typeList.ListType = list
-
- t.Type = typeList
-
- return t
-}
-
-func List(t Type) *listType {
- return &listType{
- itemType: t,
- }
-}
-
-type setType struct {
- itemType Type
-}
-
-func (v *setType) String() string {
- return v.Yql()
-}
-
-func (v *setType) Yql() string {
- return "Set<" + v.itemType.Yql() + ">"
-}
-
-func (v *setType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(*setType)
- if !ok {
- return false
- }
-
- return v.itemType.equalsTo(vv.itemType)
-}
-
-func (v *setType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- typeDict := a.TypeDict()
-
- typeDict.DictType = a.Dict()
-
- typeDict.DictType.Key = v.itemType.toYDB(a)
- typeDict.DictType.Payload = _voidType
-
- t.Type = typeDict
-
- return t
-}
-
-func Set(t Type) *setType {
- return &setType{
- itemType: t,
- }
-}
-
-type optionalType struct {
- innerType Type
-}
-
-func (v optionalType) IsOptional() {}
-
-func (v optionalType) InnerType() Type {
- return v.innerType
-}
-
-func (v optionalType) String() string {
- return v.Yql()
-}
-
-func (v optionalType) Yql() string {
- return "Optional<" + v.innerType.Yql() + ">"
-}
-
-func (v optionalType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(optionalType)
- if !ok {
- return false
- }
-
- return v.innerType.equalsTo(vv.innerType)
-}
-
-func (v optionalType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- typeOptional := a.TypeOptional()
-
- typeOptional.OptionalType = a.Optional()
-
- typeOptional.OptionalType.Item = v.innerType.toYDB(a)
-
- t.Type = typeOptional
-
- return t
-}
-
-func Optional(t Type) optionalType {
- return optionalType{
- innerType: t,
- }
-}
-
-type PrimitiveType uint
-
-func (v PrimitiveType) String() string {
- return v.Yql()
-}
-
-func (v PrimitiveType) Yql() string {
- return primitiveString[v]
-}
-
-const (
- TypeUnknown PrimitiveType = iota
- TypeBool
- TypeInt8
- TypeUint8
- TypeInt16
- TypeUint16
- TypeInt32
- TypeUint32
- TypeInt64
- TypeUint64
- TypeFloat
- TypeDouble
- TypeDate
- TypeDatetime
- TypeTimestamp
- TypeInterval
- TypeTzDate
- TypeTzDatetime
- TypeTzTimestamp
- TypeBytes
- TypeText
- TypeYSON
- TypeJSON
- TypeUUID
- TypeJSONDocument
- TypeDyNumber
-)
-
-var primitive = [...]*Ydb.Type{
- TypeBool: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_BOOL}},
- TypeInt8: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT8}},
- TypeUint8: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT8}},
- TypeInt16: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT16}},
- TypeUint16: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT16}},
- TypeInt32: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT32}},
- TypeUint32: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT32}},
- TypeInt64: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INT64}},
- TypeUint64: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UINT64}},
- TypeFloat: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_FLOAT}},
- TypeDouble: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DOUBLE}},
- TypeDate: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATE}},
- TypeDatetime: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DATETIME}},
- TypeTimestamp: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TIMESTAMP}},
- TypeInterval: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_INTERVAL}},
- TypeTzDate: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATE}},
- TypeTzDatetime: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_DATETIME}},
- TypeTzTimestamp: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_TZ_TIMESTAMP}},
- TypeBytes: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_STRING}},
- TypeText: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UTF8}},
- TypeYSON: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_YSON}},
- TypeJSON: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON}},
- TypeUUID: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_UUID}},
- TypeJSONDocument: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_JSON_DOCUMENT}},
- TypeDyNumber: {Type: &Ydb.Type_TypeId{TypeId: Ydb.Type_DYNUMBER}},
-}
-
-var primitiveString = [...]string{
- TypeUnknown: "",
- TypeBool: "Bool",
- TypeInt8: "Int8",
- TypeUint8: "Uint8",
- TypeInt16: "Int16",
- TypeUint16: "Uint16",
- TypeInt32: "Int32",
- TypeUint32: "Uint32",
- TypeInt64: "Int64",
- TypeUint64: "Uint64",
- TypeFloat: "Float",
- TypeDouble: "Double",
- TypeDate: "Date",
- TypeDatetime: "Datetime",
- TypeTimestamp: "Timestamp",
- TypeInterval: "Interval",
- TypeTzDate: "TzDate",
- TypeTzDatetime: "TzDatetime",
- TypeTzTimestamp: "TzTimestamp",
- TypeBytes: "String",
- TypeText: "Utf8",
- TypeYSON: "Yson",
- TypeJSON: "Json",
- TypeUUID: "Uuid",
- TypeJSONDocument: "JsonDocument",
- TypeDyNumber: "DyNumber",
-}
-
-func (v PrimitiveType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(PrimitiveType)
- if !ok {
- return false
- }
-
- return v == vv
-}
-
-func (v PrimitiveType) toYDB(*allocator.Allocator) *Ydb.Type {
- return primitive[v]
-}
-
-type (
- StructField struct {
- Name string
- T Type
- }
- StructType struct {
- fields []StructField
- }
-)
-
-func (v *StructType) String() string {
- return v.Yql()
-}
-
-func (v *StructType) Yql() string {
- buffer := xstring.Buffer()
- defer buffer.Free()
- buffer.WriteString("Struct<")
- for i := range v.fields {
- if i > 0 {
- buffer.WriteByte(',')
- }
- buffer.WriteString("'" + v.fields[i].Name + "'")
- buffer.WriteByte(':')
- buffer.WriteString(v.fields[i].T.Yql())
- }
- buffer.WriteByte('>')
-
- return buffer.String()
-}
-
-func (v *StructType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(*StructType)
- if !ok {
- return false
- }
- if len(v.fields) != len(vv.fields) {
- return false
- }
- for i := range v.fields {
- if v.fields[i].Name != vv.fields[i].Name {
- return false
- }
- if !v.fields[i].T.equalsTo(vv.fields[i].T) {
- return false
- }
- }
-
- return true
-}
-
-func (v *StructType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- typeStruct := a.TypeStruct()
-
- typeStruct.StructType = a.Struct()
-
- for i := range v.fields {
- structMember := a.StructMember()
- structMember.Name = v.fields[i].Name
- structMember.Type = v.fields[i].T.toYDB(a)
- typeStruct.StructType.Members = append(
- typeStruct.StructType.Members,
- structMember,
- )
- }
-
- t.Type = typeStruct
-
- return t
-}
-
-func Struct(fields ...StructField) (v *StructType) {
- return &StructType{
- fields: fields,
- }
-}
-
-func StructFields(ms []*Ydb.StructMember) []StructField {
- fs := make([]StructField, len(ms))
- for i, m := range ms {
- fs[i] = StructField{
- Name: m.Name,
- T: TypeFromYDB(m.Type),
- }
- }
-
- return fs
-}
-
-type TupleType struct {
- items []Type
-}
-
-func (v *TupleType) String() string {
- return v.Yql()
-}
-
-func (v *TupleType) Yql() string {
- buffer := xstring.Buffer()
- defer buffer.Free()
- buffer.WriteString("Tuple<")
- for i, t := range v.items {
- if i > 0 {
- buffer.WriteByte(',')
- }
- buffer.WriteString(t.Yql())
- }
- buffer.WriteByte('>')
-
- return buffer.String()
-}
-
-func (v *TupleType) equalsTo(rhs Type) bool {
- vv, ok := rhs.(*TupleType)
- if !ok {
- return false
- }
- if len(v.items) != len(vv.items) {
- return false
- }
- for i := range v.items {
- if !v.items[i].equalsTo(vv.items[i]) {
- return false
- }
- }
-
- return true
-}
-
-func (v *TupleType) toYDB(a *allocator.Allocator) *Ydb.Type {
- var items []Type
- if v != nil {
- items = v.items
- }
- t := a.Type()
-
- typeTuple := a.TypeTuple()
-
- typeTuple.TupleType = a.Tuple()
-
- for _, vv := range items {
- typeTuple.TupleType.Elements = append(typeTuple.TupleType.Elements, vv.toYDB(a))
- }
-
- t.Type = typeTuple
-
- return t
-}
-
-func Tuple(items ...Type) (v *TupleType) {
- return &TupleType{
- items: items,
- }
-}
-
-type variantStructType struct {
- *StructType
-}
-
-func (v *variantStructType) Yql() string {
- buffer := xstring.Buffer()
- defer buffer.Free()
- buffer.WriteString("Variant<")
- for i := range v.fields {
- if i > 0 {
- buffer.WriteByte(',')
- }
- buffer.WriteString("'" + v.fields[i].Name + "'")
- buffer.WriteByte(':')
- buffer.WriteString(v.fields[i].T.Yql())
- }
- buffer.WriteByte('>')
-
- return buffer.String()
-}
-
-func (v *variantStructType) equalsTo(rhs Type) bool {
- switch t := rhs.(type) {
- case *variantStructType:
- return v.StructType.equalsTo(t.StructType)
- case *StructType:
- return v.StructType.equalsTo(t)
- default:
- return false
- }
-}
-
-func (v *variantStructType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- typeVariant := a.TypeVariant()
-
- typeVariant.VariantType = a.Variant()
-
- structItems := a.VariantStructItems()
- structItems.StructItems = v.StructType.toYDB(a).Type.(*Ydb.Type_StructType).StructType
-
- typeVariant.VariantType.Type = structItems
-
- t.Type = typeVariant
-
- return t
-}
-
-func VariantStruct(fields ...StructField) *variantStructType {
- return &variantStructType{
- StructType: Struct(fields...),
- }
-}
-
-type variantTupleType struct {
- *TupleType
-}
-
-func (v *variantTupleType) Yql() string {
- buffer := xstring.Buffer()
- defer buffer.Free()
- buffer.WriteString("Variant<")
- for i, t := range v.items {
- if i > 0 {
- buffer.WriteByte(',')
- }
- buffer.WriteString(t.Yql())
- }
- buffer.WriteByte('>')
-
- return buffer.String()
-}
-
-func (v *variantTupleType) equalsTo(rhs Type) bool {
- switch t := rhs.(type) {
- case *variantTupleType:
- return v.TupleType.equalsTo(t.TupleType)
- case *TupleType:
- return v.TupleType.equalsTo(t)
- default:
- return false
- }
-}
-
-func (v *variantTupleType) toYDB(a *allocator.Allocator) *Ydb.Type {
- t := a.Type()
-
- typeVariant := a.TypeVariant()
-
- typeVariant.VariantType = a.Variant()
-
- tupleItems := a.VariantTupleItems()
- tupleItems.TupleItems = v.TupleType.toYDB(a).Type.(*Ydb.Type_TupleType).TupleType
-
- typeVariant.VariantType.Type = tupleItems
-
- t.Type = typeVariant
-
- return t
-}
-
-func VariantTuple(items ...Type) *variantTupleType {
- return &variantTupleType{
- TupleType: Tuple(items...),
- }
-}
-
-type voidType struct{}
-
-func (v voidType) String() string {
- return v.Yql()
-}
-
-func (v voidType) Yql() string {
- return "Void"
-}
-
-var _voidType = &Ydb.Type{
- Type: &Ydb.Type_VoidType{},
-}
-
-func (v voidType) equalsTo(rhs Type) bool {
- _, ok := rhs.(voidType)
-
- return ok
-}
-
-func (voidType) toYDB(*allocator.Allocator) *Ydb.Type {
- return _voidType
-}
-
-func Void() voidType {
- return voidType{}
-}
-
-type nullType struct{}
-
-func (v nullType) String() string {
- return v.Yql()
-}
-
-func (v nullType) Yql() string {
- return "Null"
-}
-
-var _nullType = &Ydb.Type{
- Type: &Ydb.Type_NullType{},
-}
-
-func (v nullType) equalsTo(rhs Type) bool {
- _, ok := rhs.(nullType)
-
- return ok
-}
-
-func (nullType) toYDB(*allocator.Allocator) *Ydb.Type {
- return _nullType
-}
-
-func Null() nullType {
- return nullType{}
-}
diff --git a/internal/value/type_test.go b/internal/value/type_test.go
deleted file mode 100644
index 22236684e..000000000
--- a/internal/value/type_test.go
+++ /dev/null
@@ -1,275 +0,0 @@
-package value
-
-import (
- "testing"
-)
-
-func TestTypeToString(t *testing.T) {
- for _, tt := range []struct {
- t Type
- s string
- }{
- {
- t: Void(),
- s: "Void",
- },
- {
- t: Null(),
- s: "Null",
- },
- {
- t: TypeBool,
- s: "Bool",
- },
- {
- t: TypeInt8,
- s: "Int8",
- },
- {
- t: TypeUint8,
- s: "Uint8",
- },
- {
- t: TypeInt16,
- s: "Int16",
- },
- {
- t: TypeUint16,
- s: "Uint16",
- },
- {
- t: TypeInt32,
- s: "Int32",
- },
- {
- t: TypeUint32,
- s: "Uint32",
- },
- {
- t: TypeInt64,
- s: "Int64",
- },
- {
- t: TypeUint64,
- s: "Uint64",
- },
- {
- t: TypeFloat,
- s: "Float",
- },
- {
- t: TypeDouble,
- s: "Double",
- },
- {
- t: TypeDate,
- s: "Date",
- },
- {
- t: TypeDatetime,
- s: "Datetime",
- },
- {
- t: TypeTimestamp,
- s: "Timestamp",
- },
- {
- t: TypeInterval,
- s: "Interval",
- },
- {
- t: TypeTzDate,
- s: "TzDate",
- },
- {
- t: TypeTzDatetime,
- s: "TzDatetime",
- },
- {
- t: TypeTzTimestamp,
- s: "TzTimestamp",
- },
- {
- t: TypeBytes,
- s: "String",
- },
- {
- t: TypeText,
- s: "Utf8",
- },
- {
- t: TypeYSON,
- s: "Yson",
- },
- {
- t: TypeJSON,
- s: "Json",
- },
- {
- t: TypeUUID,
- s: "Uuid",
- },
- {
- t: TypeJSONDocument,
- s: "JsonDocument",
- },
- {
- t: TypeDyNumber,
- s: "DyNumber",
- },
- {
- t: Optional(TypeBool),
- s: "Optional",
- },
- {
- t: Optional(TypeInt8),
- s: "Optional",
- },
- {
- t: Optional(TypeUint8),
- s: "Optional",
- },
- {
- t: Optional(TypeInt16),
- s: "Optional",
- },
- {
- t: Optional(TypeUint16),
- s: "Optional",
- },
- {
- t: Optional(TypeInt32),
- s: "Optional",
- },
- {
- t: Optional(TypeUint32),
- s: "Optional",
- },
- {
- t: Optional(TypeInt64),
- s: "Optional",
- },
- {
- t: Optional(TypeUint64),
- s: "Optional",
- },
- {
- t: Optional(TypeFloat),
- s: "Optional",
- },
- {
- t: Optional(TypeDouble),
- s: "Optional",
- },
- {
- t: Optional(TypeDate),
- s: "Optional",
- },
- {
- t: Optional(TypeDatetime),
- s: "Optional",
- },
- {
- t: Optional(TypeTimestamp),
- s: "Optional",
- },
- {
- t: Optional(TypeInterval),
- s: "Optional",
- },
- {
- t: Optional(TypeTzDate),
- s: "Optional",
- },
- {
- t: Optional(TypeTzDatetime),
- s: "Optional",
- },
- {
- t: Optional(TypeTzTimestamp),
- s: "Optional",
- },
- {
- t: Optional(TypeBytes),
- s: "Optional",
- },
- {
- t: Optional(TypeText),
- s: "Optional",
- },
- {
- t: Optional(TypeYSON),
- s: "Optional",
- },
- {
- t: Optional(TypeJSON),
- s: "Optional",
- },
- {
- t: Optional(TypeUUID),
- s: "Optional",
- },
- {
- t: Optional(TypeJSONDocument),
- s: "Optional",
- },
- {
- t: Optional(TypeDyNumber),
- s: "Optional",
- },
- {
- t: Decimal(22, 9),
- s: "Decimal(22,9)",
- },
- {
- t: Dict(TypeText, TypeTimestamp),
- s: "Dict",
- },
- {
- t: EmptyList(),
- s: "EmptyList",
- },
- {
- t: List(TypeUint32),
- s: "List",
- },
- {
- t: Set(TypeUint32),
- s: "Set",
- },
- {
- t: EmptySet(),
- s: "EmptyDict",
- },
- {
- t: EmptyDict(),
- s: "EmptyDict",
- },
- {
- t: VariantStruct(
- StructField{
- Name: "a",
- T: TypeBool,
- },
- StructField{
- Name: "b",
- T: TypeFloat,
- },
- ),
- s: "Variant<'a':Bool,'b':Float>",
- },
- {
- t: VariantTuple(
- TypeBool,
- TypeFloat,
- ),
- s: "Variant",
- },
- } {
- t.Run(tt.s, func(t *testing.T) {
- if got := tt.t.Yql(); got != tt.s {
- t.Errorf("s representations not equals:\n\n - got: %s\n\n - want: %s", got, tt.s)
- }
- })
- }
-}
diff --git a/internal/value/value.go b/internal/value/value.go
index c86483d65..32a2b7b60 100644
--- a/internal/value/value.go
+++ b/internal/value/value.go
@@ -2,9 +2,9 @@ package value
import (
"encoding/binary"
- "errors"
"fmt"
"math/big"
+ "reflect"
"sort"
"strconv"
"time"
@@ -14,6 +14,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/decimal"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
)
@@ -24,7 +25,7 @@ const (
)
type Value interface {
- Type() Type
+ Type() types.Type
Yql() string
castTo(dst interface{}) error
@@ -34,7 +35,7 @@ type Value interface {
func ToYDB(v Value, a *allocator.Allocator) *Ydb.TypedValue {
tv := a.TypedValue()
- tv.Type = v.Type().toYDB(a)
+ tv.Type = v.Type().ToYDB(a)
tv.Value = v.toYDB(a)
return tv
@@ -57,16 +58,16 @@ func FromYDB(t *Ydb.Type, v *Ydb.Value) Value {
return vv
}
-func nullValueFromYDB(x *Ydb.Value, t Type) (_ Value, ok bool) {
+func nullValueFromYDB(x *Ydb.Value, t types.Type) (_ Value, ok bool) {
for {
- switch xx := x.Value.(type) {
+ switch xx := x.GetValue().(type) {
case *Ydb.Value_NestedValue:
x = xx.NestedValue
case *Ydb.Value_NullFlagValue:
switch tt := t.(type) {
- case optionalType:
- return NullValue(tt.innerType), true
- case voidType:
+ case types.Optional:
+ return NullValue(tt.InnerType()), true
+ case types.Void:
return VoidValue(), true
default:
return nil, false
@@ -77,57 +78,57 @@ func nullValueFromYDB(x *Ydb.Value, t Type) (_ Value, ok bool) {
}
}
-func primitiveValueFromYDB(t PrimitiveType, v *Ydb.Value) (Value, error) {
+func primitiveValueFromYDB(t types.Primitive, v *Ydb.Value) (Value, error) {
switch t {
- case TypeBool:
+ case types.Bool:
return BoolValue(v.GetBoolValue()), nil
- case TypeInt8:
+ case types.Int8:
return Int8Value(int8(v.GetInt32Value())), nil
- case TypeInt16:
+ case types.Int16:
return Int16Value(int16(v.GetInt32Value())), nil
- case TypeInt32:
+ case types.Int32:
return Int32Value(v.GetInt32Value()), nil
- case TypeInt64:
+ case types.Int64:
return Int64Value(v.GetInt64Value()), nil
- case TypeUint8:
+ case types.Uint8:
return Uint8Value(uint8(v.GetUint32Value())), nil
- case TypeUint16:
+ case types.Uint16:
return Uint16Value(uint16(v.GetUint32Value())), nil
- case TypeUint32:
+ case types.Uint32:
return Uint32Value(v.GetUint32Value()), nil
- case TypeUint64:
+ case types.Uint64:
return Uint64Value(v.GetUint64Value()), nil
- case TypeDate:
+ case types.Date:
return DateValue(v.GetUint32Value()), nil
- case TypeDatetime:
+ case types.Datetime:
return DatetimeValue(v.GetUint32Value()), nil
- case TypeInterval:
+ case types.Interval:
return IntervalValue(v.GetInt64Value()), nil
- case TypeTimestamp:
+ case types.Timestamp:
return TimestampValue(v.GetUint64Value()), nil
- case TypeFloat:
+ case types.Float:
return FloatValue(v.GetFloatValue()), nil
- case TypeDouble:
+ case types.Double:
return DoubleValue(v.GetDoubleValue()), nil
- case TypeText:
+ case types.Text:
return TextValue(v.GetTextValue()), nil
- case TypeYSON:
+ case types.YSON:
switch vv := v.GetValue().(type) {
case *Ydb.Value_TextValue:
return YSONValue(xstring.ToBytes(vv.TextValue)), nil
@@ -137,29 +138,29 @@ func primitiveValueFromYDB(t PrimitiveType, v *Ydb.Value) (Value, error) {
return nil, xerrors.WithStackTrace(fmt.Errorf("uncovered YSON internal type: %T", vv))
}
- case TypeJSON:
+ case types.JSON:
return JSONValue(v.GetTextValue()), nil
- case TypeJSONDocument:
+ case types.JSONDocument:
return JSONDocumentValue(v.GetTextValue()), nil
- case TypeDyNumber:
+ case types.DyNumber:
return DyNumberValue(v.GetTextValue()), nil
- case TypeTzDate:
+ case types.TzDate:
return TzDateValue(v.GetTextValue()), nil
- case TypeTzDatetime:
+ case types.TzDatetime:
return TzDatetimeValue(v.GetTextValue()), nil
- case TypeTzTimestamp:
+ case types.TzTimestamp:
return TzTimestampValue(v.GetTextValue()), nil
- case TypeBytes:
+ case types.Bytes:
return BytesValue(v.GetBytesValue()), nil
- case TypeUUID:
- return UUIDValue(BigEndianUint128(v.High_128, v.GetLow_128())), nil
+ case types.UUID:
+ return UUIDValue(BigEndianUint128(v.GetHigh_128(), v.GetLow_128())), nil
default:
return nil, xerrors.WithStackTrace(fmt.Errorf("uncovered primitive type: %T", t))
@@ -167,125 +168,133 @@ func primitiveValueFromYDB(t PrimitiveType, v *Ydb.Value) (Value, error) {
}
func fromYDB(t *Ydb.Type, v *Ydb.Value) (Value, error) {
- tt := TypeFromYDB(t)
+ tt := types.TypeFromYDB(t)
if vv, ok := nullValueFromYDB(v, tt); ok {
return vv, nil
}
switch ttt := tt.(type) {
- case PrimitiveType:
+ case types.Primitive:
return primitiveValueFromYDB(ttt, v)
- case voidType:
+ case types.Void:
return VoidValue(), nil
- case nullType:
+ case types.Null:
return NullValue(tt), nil
- case *DecimalType:
- return DecimalValue(BigEndianUint128(v.High_128, v.GetLow_128()), ttt.Precision, ttt.Scale), nil
+ case *types.Decimal:
+ return DecimalValue(BigEndianUint128(v.GetHigh_128(), v.GetLow_128()), ttt.Precision(), ttt.Scale()), nil
- case optionalType:
- t = t.Type.(*Ydb.Type_OptionalType).OptionalType.Item
- if nestedValue, ok := v.Value.(*Ydb.Value_NestedValue); ok {
+ case types.Optional:
+ t = t.GetType().(*Ydb.Type_OptionalType).OptionalType.GetItem()
+ if nestedValue, ok := v.GetValue().(*Ydb.Value_NestedValue); ok {
return OptionalValue(FromYDB(t, nestedValue.NestedValue)), nil
}
return OptionalValue(FromYDB(t, v)), nil
- case *listType:
+ case *types.List:
return ListValue(func() []Value {
vv := make([]Value, len(v.GetItems()))
a := allocator.New()
defer a.Free()
for i, vvv := range v.GetItems() {
- vv[i] = FromYDB(ttt.itemType.toYDB(a), vvv)
+ vv[i] = FromYDB(ttt.ItemType().ToYDB(a), vvv)
}
return vv
}()...), nil
- case *TupleType:
+ case *types.Tuple:
return TupleValue(func() []Value {
vv := make([]Value, len(v.GetItems()))
a := allocator.New()
defer a.Free()
for i, vvv := range v.GetItems() {
- vv[i] = FromYDB(ttt.items[i].toYDB(a), vvv)
+ vv[i] = FromYDB(ttt.ItemType(i).ToYDB(a), vvv)
}
return vv
}()...), nil
- case *StructType:
+ case *types.Struct:
return StructValue(func() []StructValueField {
vv := make([]StructValueField, len(v.GetItems()))
a := allocator.New()
defer a.Free()
for i, vvv := range v.GetItems() {
vv[i] = StructValueField{
- Name: ttt.fields[i].Name,
- V: FromYDB(ttt.fields[i].T.toYDB(a), vvv),
+ Name: ttt.Field(i).Name,
+ V: FromYDB(ttt.Field(i).T.ToYDB(a), vvv),
}
}
return vv
}()...), nil
- case *dictType:
+ case *types.Dict:
return DictValue(func() []DictValueField {
vv := make([]DictValueField, len(v.GetPairs()))
a := allocator.New()
defer a.Free()
for i, vvv := range v.GetPairs() {
vv[i] = DictValueField{
- K: FromYDB(ttt.keyType.toYDB(a), vvv.Key),
- V: FromYDB(ttt.valueType.toYDB(a), vvv.Payload),
+ K: FromYDB(ttt.KeyType().ToYDB(a), vvv.GetKey()),
+ V: FromYDB(ttt.ValueType().ToYDB(a), vvv.GetPayload()),
}
}
return vv
}()...), nil
- case *setType:
+ case *types.Set:
return SetValue(func() []Value {
vv := make([]Value, len(v.GetPairs()))
a := allocator.New()
defer a.Free()
for i, vvv := range v.GetPairs() {
- vv[i] = FromYDB(ttt.itemType.toYDB(a), vvv.Key)
+ vv[i] = FromYDB(ttt.ItemType().ToYDB(a), vvv.GetKey())
}
return vv
}()...), nil
- case *variantStructType:
+ case *types.VariantStruct:
a := allocator.New()
defer a.Free()
return VariantValueStruct(
FromYDB(
- ttt.StructType.fields[v.VariantIndex].T.toYDB(a),
- v.Value.(*Ydb.Value_NestedValue).NestedValue,
+ ttt.Struct.Field(int(v.GetVariantIndex())).T.ToYDB(a),
+ v.GetValue().(*Ydb.Value_NestedValue).NestedValue,
),
- ttt.StructType.fields[v.VariantIndex].Name,
- ttt.StructType,
+ ttt.Struct.Field(int(v.GetVariantIndex())).Name,
+ ttt.Struct,
), nil
- case *variantTupleType:
+ case *types.VariantTuple:
a := allocator.New()
defer a.Free()
return VariantValueTuple(
FromYDB(
- ttt.TupleType.items[v.VariantIndex].toYDB(a),
- v.Value.(*Ydb.Value_NestedValue).NestedValue,
+ ttt.Tuple.ItemType(int(v.GetVariantIndex())).ToYDB(a),
+ v.GetValue().(*Ydb.Value_NestedValue).NestedValue,
),
- v.VariantIndex,
- ttt.TupleType,
+ v.GetVariantIndex(),
+ ttt.Tuple,
), nil
+ case *types.PgType:
+ return &pgValue{
+ t: types.PgType{
+ OID: ttt.OID,
+ },
+ val: v.GetTextValue(),
+ }, nil
+
default:
return nil, xerrors.WithStackTrace(fmt.Errorf("uncovered type: %T", ttt))
}
@@ -304,7 +313,10 @@ func (v boolValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -312,8 +324,8 @@ func (v boolValue) Yql() string {
return strconv.FormatBool(bool(v))
}
-func (boolValue) Type() Type {
- return TypeBool
+func (boolValue) Type() types.Type {
+ return types.Bool
}
func (v boolValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -352,7 +364,10 @@ func (v dateValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -360,8 +375,8 @@ func (v dateValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), DateToTime(uint32(v)).UTC().Format(LayoutDate))
}
-func (dateValue) Type() Type {
- return TypeDate
+func (dateValue) Type() types.Type {
+ return types.Date
}
func (v dateValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -405,7 +420,10 @@ func (v datetimeValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -413,8 +431,8 @@ func (v datetimeValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), DatetimeToTime(uint32(v)).UTC().Format(LayoutDatetime))
}
-func (datetimeValue) Type() Type {
- return TypeDatetime
+func (datetimeValue) Type() types.Type {
+ return types.Datetime
}
func (v datetimeValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -440,7 +458,7 @@ var _ DecimalValuer = (*decimalValue)(nil)
type decimalValue struct {
value [16]byte
- innerType *DecimalType
+ innerType *types.Decimal
}
func (v *decimalValue) Value() [16]byte {
@@ -448,11 +466,11 @@ func (v *decimalValue) Value() [16]byte {
}
func (v *decimalValue) Precision() uint32 {
- return v.innerType.Precision
+ return v.innerType.Precision()
}
func (v *decimalValue) Scale() uint32 {
- return v.innerType.Scale
+ return v.innerType.Scale()
}
type DecimalValuer interface {
@@ -462,7 +480,10 @@ type DecimalValuer interface {
}
func (v *decimalValue) castTo(dst interface{}) error {
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' to '%T' destination", v, dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' to '%T' destination",
+ ErrCannotCast, v, dst,
+ ))
}
func (v *decimalValue) Yql() string {
@@ -471,19 +492,19 @@ func (v *decimalValue) Yql() string {
buffer.WriteString(v.innerType.Name())
buffer.WriteByte('(')
buffer.WriteByte('"')
- s := decimal.FromBytes(v.value[:], v.innerType.Precision, v.innerType.Scale).String()
- buffer.WriteString(s[:len(s)-int(v.innerType.Scale)] + "." + s[len(s)-int(v.innerType.Scale):])
+ s := decimal.FromBytes(v.value[:], v.innerType.Precision(), v.innerType.Scale()).String()
+ buffer.WriteString(s[:len(s)-int(v.innerType.Scale())] + "." + s[len(s)-int(v.innerType.Scale()):])
buffer.WriteByte('"')
buffer.WriteByte(',')
- buffer.WriteString(strconv.FormatUint(uint64(v.innerType.Precision), 10))
+ buffer.WriteString(strconv.FormatUint(uint64(v.innerType.Precision()), 10))
buffer.WriteByte(',')
- buffer.WriteString(strconv.FormatUint(uint64(v.innerType.Scale), 10))
+ buffer.WriteString(strconv.FormatUint(uint64(v.innerType.Scale()), 10))
buffer.WriteByte(')')
return buffer.String()
}
-func (v *decimalValue) Type() Type {
+func (v *decimalValue) Type() types.Type {
return v.innerType
}
@@ -511,10 +532,10 @@ func DecimalValueFromBigInt(v *big.Int, precision, scale uint32) *decimalValue {
func DecimalValue(v [16]byte, precision, scale uint32) *decimalValue {
return &decimalValue{
value: v,
- innerType: &DecimalType{
- Precision: precision,
- Scale: scale,
- },
+ innerType: types.NewDecimal(
+ precision,
+ scale,
+ ),
}
}
@@ -524,7 +545,7 @@ type (
V Value
}
dictValue struct {
- t Type
+ t types.Type
values []DictValueField
}
)
@@ -539,7 +560,10 @@ func (v *dictValue) DictValues() map[Value]Value {
}
func (v *dictValue) castTo(dst interface{}) error {
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' to '%T' destination", v, dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' to '%T' destination",
+ ErrCannotCast, v, dst,
+ ))
}
func (v *dictValue) Yql() string {
@@ -559,7 +583,7 @@ func (v *dictValue) Yql() string {
return buffer.String()
}
-func (v *dictValue) Type() Type {
+func (v *dictValue) Type() types.Type {
return v.t
}
@@ -576,7 +600,7 @@ func (v *dictValue) toYDB(a *allocator.Allocator) *Ydb.Value {
pair.Key = values[i].K.toYDB(a)
pair.Payload = values[i].V.toYDB(a)
- vvv.Pairs = append(vvv.Pairs, pair)
+ vvv.Pairs = append(vvv.GetPairs(), pair)
}
return vvv
@@ -586,12 +610,12 @@ func DictValue(values ...DictValueField) *dictValue {
sort.Slice(values, func(i, j int) bool {
return values[i].K.Yql() < values[j].K.Yql()
})
- var t Type
+ var t types.Type
switch {
case len(values) > 0:
- t = Dict(values[0].K.Type(), values[0].V.Type())
+ t = types.NewDict(values[0].K.Type(), values[0].V.Type())
default:
- t = EmptyDict()
+ t = types.NewEmptyDict()
}
return &dictValue{
@@ -619,7 +643,10 @@ func (v *doubleValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -627,8 +654,8 @@ func (v *doubleValue) Yql() string {
return fmt.Sprintf("%s(\"%v\")", v.Type().Yql(), v.value)
}
-func (*doubleValue) Type() Type {
- return TypeDouble
+func (*doubleValue) Type() types.Type {
+ return types.Double
}
func (v *doubleValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -660,7 +687,10 @@ func (v dyNumberValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -668,8 +698,8 @@ func (v dyNumberValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), string(v))
}
-func (dyNumberValue) Type() Type {
- return TypeDyNumber
+func (dyNumberValue) Type() types.Type {
+ return types.DyNumber
}
func (v dyNumberValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -709,7 +739,10 @@ func (v *floatValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -717,8 +750,8 @@ func (v *floatValue) Yql() string {
return fmt.Sprintf("%s(\"%v\")", v.Type().Yql(), v.value)
}
-func (*floatValue) Type() Type {
- return TypeFloat
+func (*floatValue) Type() types.Type {
+ return types.Float
}
func (v *floatValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -774,7 +807,10 @@ func (v int8Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -782,8 +818,8 @@ func (v int8Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "t"
}
-func (int8Value) Type() Type {
- return TypeInt8
+func (int8Value) Type() types.Type {
+ return types.Int8
}
func (v int8Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -833,7 +869,10 @@ func (v int16Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -841,8 +880,8 @@ func (v int16Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "s"
}
-func (int16Value) Type() Type {
- return TypeInt16
+func (int16Value) Type() types.Type {
+ return types.Int16
}
func (v int16Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -892,7 +931,10 @@ func (v int32Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -900,8 +942,8 @@ func (v int32Value) Yql() string {
return strconv.FormatInt(int64(v), 10)
}
-func (int32Value) Type() Type {
- return TypeInt32
+func (int32Value) Type() types.Type {
+ return types.Int32
}
func (v int32Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -939,7 +981,10 @@ func (v int64Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -947,8 +992,8 @@ func (v int64Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "l"
}
-func (int64Value) Type() Type {
- return TypeInt64
+func (int64Value) Type() types.Type {
+ return types.Int64
}
func (v int64Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -978,7 +1023,10 @@ func (v intervalValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1024,8 +1072,8 @@ func (v intervalValue) Yql() string {
return buffer.String()
}
-func (intervalValue) Type() Type {
- return TypeInterval
+func (intervalValue) Type() types.Type {
+ return types.Interval
}
func (v intervalValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1060,7 +1108,10 @@ func (v jsonValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1068,8 +1119,8 @@ func (v jsonValue) Yql() string {
return fmt.Sprintf("%s(@@%s@@)", v.Type().Yql(), string(v))
}
-func (jsonValue) Type() Type {
- return TypeJSON
+func (jsonValue) Type() types.Type {
+ return types.JSON
}
func (v jsonValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1099,7 +1150,10 @@ func (v jsonDocumentValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1107,8 +1161,8 @@ func (v jsonDocumentValue) Yql() string {
return fmt.Sprintf("%s(@@%s@@)", v.Type().Yql(), string(v))
}
-func (jsonDocumentValue) Type() Type {
- return TypeJSONDocument
+func (jsonDocumentValue) Type() types.Type {
+ return types.JSONDocument
}
func (v jsonDocumentValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1126,7 +1180,7 @@ func JSONDocumentValue(v string) jsonDocumentValue {
}
type listValue struct {
- t Type
+ t types.Type
items []Value
}
@@ -1135,7 +1189,10 @@ func (v *listValue) ListItems() []Value {
}
func (v *listValue) castTo(dst interface{}) error {
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), dst,
+ ))
}
func (v *listValue) Yql() string {
@@ -1153,7 +1210,7 @@ func (v *listValue) Yql() string {
return buffer.String()
}
-func (v *listValue) Type() Type {
+func (v *listValue) Type() types.Type {
return v.t
}
@@ -1165,19 +1222,19 @@ func (v *listValue) toYDB(a *allocator.Allocator) *Ydb.Value {
vvv := a.Value()
for _, vv := range items {
- vvv.Items = append(vvv.Items, vv.toYDB(a))
+ vvv.Items = append(vvv.GetItems(), vv.toYDB(a))
}
return vvv
}
func ListValue(items ...Value) *listValue {
- var t Type
+ var t types.Type
switch {
case len(items) > 0:
- t = List(items[0].Type())
+ t = types.NewList(items[0].Type())
default:
- t = EmptyList()
+ t = types.NewEmptyList()
}
return &listValue{
@@ -1186,13 +1243,49 @@ func ListValue(items ...Value) *listValue {
}
}
+type pgValue struct {
+ t types.PgType
+ val string
+}
+
+func (v pgValue) castTo(dst interface{}) error {
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w PgType to '%T' destination",
+ ErrCannotCast, dst,
+ ))
+}
+
+func (v pgValue) Type() types.Type {
+ return v.t
+}
+
+func (v pgValue) toYDB(_ *allocator.Allocator) *Ydb.Value {
+ //nolint:godox
+ // TODO: make allocator
+ return &Ydb.Value{
+ Value: &Ydb.Value_TextValue{
+ TextValue: v.val,
+ },
+ }
+}
+
+func (v pgValue) Yql() string {
+ //nolint:godox
+ // TODO: call special function for unknown oids
+ // https://github.com/ydb-platform/ydb/issues/2706
+ return fmt.Sprintf(`PgConst("%v", PgType(%v))`, v.val, v.t.OID)
+}
+
type setValue struct {
- t Type
+ t types.Type
items []Value
}
func (v *setValue) castTo(dst interface{}) error {
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' to '%T' destination", v, dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' to '%T' destination",
+ ErrCannotCast, v, dst,
+ ))
}
func (v *setValue) Yql() string {
@@ -1210,7 +1303,7 @@ func (v *setValue) Yql() string {
return buffer.String()
}
-func (v *setValue) Type() Type {
+func (v *setValue) Type() types.Type {
return v.t
}
@@ -1223,23 +1316,32 @@ func (v *setValue) toYDB(a *allocator.Allocator) *Ydb.Value {
pair.Key = vv.toYDB(a)
pair.Payload = _voidValue
- vvv.Pairs = append(vvv.Pairs, pair)
+ vvv.Pairs = append(vvv.GetPairs(), pair)
}
return vvv
}
+func PgValue(oid uint32, val string) pgValue {
+ return pgValue{
+ t: types.PgType{
+ OID: oid,
+ },
+ val: val,
+ }
+}
+
func SetValue(items ...Value) *setValue {
sort.Slice(items, func(i, j int) bool {
return items[i].Yql() < items[j].Yql()
})
- var t Type
+ var t types.Type
switch {
case len(items) > 0:
- t = Set(items[0].Type())
+ t = types.NewSet(items[0].Type())
default:
- t = EmptySet()
+ t = types.EmptySet()
}
return &setValue{
@@ -1248,26 +1350,55 @@ func SetValue(items ...Value) *setValue {
}
}
-func NullValue(t Type) *optionalValue {
+func NullValue(t types.Type) *optionalValue {
return &optionalValue{
- innerType: Optional(t),
+ innerType: types.NewOptional(t),
value: nil,
}
}
type optionalValue struct {
- innerType Type
+ innerType types.Type
value Value
}
-var errOptionalNilValue = errors.New("optional contains nil value")
-
func (v *optionalValue) castTo(dst interface{}) error {
+ ptr := reflect.ValueOf(dst)
+ if ptr.Kind() != reflect.Pointer {
+ return xerrors.WithStackTrace(fmt.Errorf("%w: '%s'", errDestinationTypeIsNotAPointer, ptr.Kind().String()))
+ }
+
+ inner := reflect.Indirect(ptr)
+
+ if inner.Kind() != reflect.Pointer {
+ if v.value == nil {
+ if ptr.CanAddr() {
+ ptr.SetZero()
+ }
+
+ return nil
+ }
+
+ if err := v.value.castTo(ptr.Interface()); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
+ }
+
if v.value == nil {
- return xerrors.WithStackTrace(errOptionalNilValue)
+ inner.SetZero()
+
+ return nil
}
- return v.value.castTo(dst)
+ inner.Set(reflect.New(inner.Type().Elem()))
+
+ if err := v.value.castTo(inner.Interface()); err != nil {
+ return xerrors.WithStackTrace(err)
+ }
+
+ return nil
}
func (v *optionalValue) Yql() string {
@@ -1278,7 +1409,7 @@ func (v *optionalValue) Yql() string {
return fmt.Sprintf("Just(%s)", v.value.Yql())
}
-func (v *optionalValue) Type() Type {
+func (v *optionalValue) Type() types.Type {
return v.innerType
}
@@ -1290,7 +1421,7 @@ func (v *optionalValue) toYDB(a *allocator.Allocator) *Ydb.Value {
vv.Value = vvv
} else {
if v.value != nil {
- vv.Value = v.value.toYDB(a).Value
+ vv = v.value.toYDB(a)
} else {
vv.Value = a.NullFlag()
}
@@ -1301,7 +1432,7 @@ func (v *optionalValue) toYDB(a *allocator.Allocator) *Ydb.Value {
func OptionalValue(v Value) *optionalValue {
return &optionalValue{
- innerType: Optional(v.Type()),
+ innerType: types.NewOptional(v.Type()),
value: v,
}
}
@@ -1312,7 +1443,7 @@ type (
V Value
}
structValue struct {
- t Type
+ t types.Type
fields []StructValueField
}
)
@@ -1327,7 +1458,10 @@ func (v *structValue) StructFields() map[string]Value {
}
func (v *structValue) castTo(dst interface{}) error {
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' to '%T' destination", v, dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' to '%T' destination",
+ ErrCannotCast, v, dst,
+ ))
}
func (v *structValue) Yql() string {
@@ -1346,7 +1480,7 @@ func (v *structValue) Yql() string {
return buffer.String()
}
-func (v *structValue) Type() Type {
+func (v *structValue) Type() types.Type {
return v.t
}
@@ -1354,7 +1488,7 @@ func (v *structValue) toYDB(a *allocator.Allocator) *Ydb.Value {
vvv := a.Value()
for i := range v.fields {
- vvv.Items = append(vvv.Items, v.fields[i].V.toYDB(a))
+ vvv.Items = append(vvv.GetItems(), v.fields[i].V.toYDB(a))
}
return vvv
@@ -1364,13 +1498,16 @@ func StructValue(fields ...StructValueField) *structValue {
sort.Slice(fields, func(i, j int) bool {
return fields[i].Name < fields[j].Name
})
- structFields := make([]StructField, 0, len(fields))
+ structFields := make([]types.StructField, 0, len(fields))
for i := range fields {
- structFields = append(structFields, StructField{fields[i].Name, fields[i].V.Type()})
+ structFields = append(structFields, types.StructField{
+ Name: fields[i].Name,
+ T: fields[i].V.Type(),
+ })
}
return &structValue{
- t: Struct(structFields...),
+ t: types.NewStruct(structFields...),
fields: fields,
}
}
@@ -1388,7 +1525,10 @@ func (v timestampValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1396,8 +1536,8 @@ func (v timestampValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), TimestampToTime(uint64(v)).UTC().Format(LayoutTimestamp))
}
-func (timestampValue) Type() Type {
- return TypeTimestamp
+func (timestampValue) Type() types.Type {
+ return types.Timestamp
}
func (v timestampValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1420,7 +1560,7 @@ func TimestampValueFromTime(t time.Time) timestampValue {
}
type tupleValue struct {
- t Type
+ t types.Type
items []Value
}
@@ -1433,7 +1573,10 @@ func (v *tupleValue) castTo(dst interface{}) error {
return v.items[0].castTo(dst)
}
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' to '%T' destination", v, dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' to '%T' destination",
+ ErrCannotCast, v, dst,
+ ))
}
func (v *tupleValue) Yql() string {
@@ -1451,7 +1594,7 @@ func (v *tupleValue) Yql() string {
return buffer.String()
}
-func (v *tupleValue) Type() Type {
+func (v *tupleValue) Type() types.Type {
return v.t
}
@@ -1463,20 +1606,20 @@ func (v *tupleValue) toYDB(a *allocator.Allocator) *Ydb.Value {
vvv := a.Value()
for _, vv := range items {
- vvv.Items = append(vvv.Items, vv.toYDB(a))
+ vvv.Items = append(vvv.GetItems(), vv.toYDB(a))
}
return vvv
}
func TupleValue(values ...Value) *tupleValue {
- tupleItems := make([]Type, 0, len(values))
+ tupleItems := make([]types.Type, 0, len(values))
for _, v := range values {
tupleItems = append(tupleItems, v.Type())
}
return &tupleValue{
- t: Tuple(tupleItems...),
+ t: types.NewTuple(tupleItems...),
items: values,
}
}
@@ -1494,7 +1637,10 @@ func (v tzDateValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1502,8 +1648,8 @@ func (v tzDateValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), string(v))
}
-func (tzDateValue) Type() Type {
- return TypeTzDate
+func (tzDateValue) Type() types.Type {
+ return types.TzDate
}
func (v tzDateValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1537,7 +1683,10 @@ func (v tzDatetimeValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1545,8 +1694,8 @@ func (v tzDatetimeValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), string(v))
}
-func (tzDatetimeValue) Type() Type {
- return TypeTzDatetime
+func (tzDatetimeValue) Type() types.Type {
+ return types.TzDatetime
}
func (v tzDatetimeValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1580,7 +1729,10 @@ func (v tzTimestampValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1588,8 +1740,8 @@ func (v tzTimestampValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), string(v))
}
-func (tzTimestampValue) Type() Type {
- return TypeTzTimestamp
+func (tzTimestampValue) Type() types.Type {
+ return types.TzTimestamp
}
func (v tzTimestampValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1659,7 +1811,10 @@ func (v uint8Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1667,8 +1822,8 @@ func (v uint8Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "ut"
}
-func (uint8Value) Type() Type {
- return TypeUint8
+func (uint8Value) Type() types.Type {
+ return types.Uint8
}
func (v uint8Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1726,7 +1881,10 @@ func (v uint16Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1734,8 +1892,8 @@ func (v uint16Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "us"
}
-func (uint16Value) Type() Type {
- return TypeUint16
+func (uint16Value) Type() types.Type {
+ return types.Uint16
}
func (v uint16Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1781,7 +1939,10 @@ func (v uint32Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1789,8 +1950,8 @@ func (v uint32Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "u"
}
-func (uint32Value) Type() Type {
- return TypeUint32
+func (uint32Value) Type() types.Type {
+ return types.Uint32
}
func (v uint32Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1824,7 +1985,10 @@ func (v uint64Value) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1832,8 +1996,8 @@ func (v uint64Value) Yql() string {
return strconv.FormatUint(uint64(v), 10) + "ul"
}
-func (uint64Value) Type() Type {
- return TypeUint64
+func (uint64Value) Type() types.Type {
+ return types.Uint64
}
func (v uint64Value) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1863,7 +2027,10 @@ func (v textValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1871,8 +2038,8 @@ func (v textValue) Yql() string {
return fmt.Sprintf("%qu", string(v))
}
-func (textValue) Type() Type {
- return TypeText
+func (textValue) Type() types.Type {
+ return types.Text
}
func (v textValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1908,7 +2075,10 @@ func (v *uuidValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -1925,8 +2095,8 @@ func (v *uuidValue) Yql() string {
return buffer.String()
}
-func (*uuidValue) Type() Type {
- return TypeUUID
+func (*uuidValue) Type() types.Type {
+ return types.UUID
}
func (v *uuidValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -1949,15 +2119,15 @@ func UUIDValue(v [16]byte) *uuidValue {
}
type variantValue struct {
- innerType Type
+ innerType types.Type
value Value
idx uint32
}
func (v *variantValue) Variant() (name string, index uint32) {
switch t := v.innerType.(type) {
- case *variantStructType:
- return t.fields[v.idx].Name, v.idx
+ case *types.VariantStruct:
+ return t.Field(int(v.idx)).Name, v.idx
default:
return "", v.idx
}
@@ -1978,9 +2148,9 @@ func (v *variantValue) Yql() string {
buffer.WriteString(v.value.Yql())
buffer.WriteByte(',')
switch t := v.innerType.(type) {
- case *variantStructType:
- fmt.Fprintf(buffer, "%q", t.fields[v.idx].Name)
- case *variantTupleType:
+ case *types.VariantStruct:
+ fmt.Fprintf(buffer, "%q", t.Field(int(v.idx)).Name)
+ case *types.VariantTuple:
fmt.Fprintf(buffer, "\""+strconv.FormatUint(uint64(v.idx), 10)+"\"")
}
buffer.WriteByte(',')
@@ -1990,7 +2160,7 @@ func (v *variantValue) Yql() string {
return buffer.String()
}
-func (v *variantValue) Type() Type {
+func (v *variantValue) Type() types.Type {
return v.innerType
}
@@ -2006,9 +2176,9 @@ func (v *variantValue) toYDB(a *allocator.Allocator) *Ydb.Value {
return vvv
}
-func VariantValueTuple(v Value, idx uint32, t Type) *variantValue {
- if tt, has := t.(*TupleType); has {
- t = VariantTuple(tt.items...)
+func VariantValueTuple(v Value, idx uint32, t types.Type) *variantValue {
+ if tt, has := t.(*types.Tuple); has {
+ t = types.NewVariantTuple(tt.InnerTypes()...)
}
return &variantValue{
@@ -2018,23 +2188,25 @@ func VariantValueTuple(v Value, idx uint32, t Type) *variantValue {
}
}
-func VariantValueStruct(v Value, name string, t Type) *variantValue {
+func VariantValueStruct(v Value, name string, t types.Type) *variantValue {
var idx int
switch tt := t.(type) {
- case *StructType:
- sort.Slice(tt.fields, func(i, j int) bool {
- return tt.fields[i].Name < tt.fields[j].Name
+ case *types.Struct:
+ fields := tt.Fields()
+ sort.Slice(fields, func(i, j int) bool {
+ return fields[i].Name < fields[j].Name
})
- idx = sort.Search(len(tt.fields), func(i int) bool {
- return tt.fields[i].Name >= name
+ idx = sort.Search(len(fields), func(i int) bool {
+ return fields[i].Name >= name
})
- t = VariantStruct(tt.fields...)
- case *variantStructType:
- sort.Slice(tt.fields, func(i, j int) bool {
- return tt.fields[i].Name < tt.fields[j].Name
+ t = types.NewVariantStruct(fields...)
+ case *types.VariantStruct:
+ fields := tt.Fields()
+ sort.Slice(fields, func(i, j int) bool {
+ return fields[i].Name < fields[j].Name
})
- idx = sort.Search(len(tt.fields), func(i int) bool {
- return tt.fields[i].Name >= name
+ idx = sort.Search(len(fields), func(i int) bool {
+ return fields[i].Name >= name
})
}
@@ -2048,7 +2220,10 @@ func VariantValueStruct(v Value, name string, t Type) *variantValue {
type voidValue struct{}
func (v voidValue) castTo(dst interface{}) error {
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%s' to '%T' destination", v.Type().Yql(), dst))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%s' to '%T' destination",
+ ErrCannotCast, v.Type().Yql(), dst,
+ ))
}
func (v voidValue) Yql() string {
@@ -2056,13 +2231,13 @@ func (v voidValue) Yql() string {
}
var (
- _voidValueType = voidType{}
+ _voidValueType = types.Void{}
_voidValue = &Ydb.Value{
Value: new(Ydb.Value_NullFlagValue),
}
)
-func (voidValue) Type() Type {
+func (voidValue) Type() types.Type {
return _voidValueType
}
@@ -2087,7 +2262,10 @@ func (v ysonValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -2095,8 +2273,8 @@ func (v ysonValue) Yql() string {
return fmt.Sprintf("%s(%q)", v.Type().Yql(), string(v))
}
-func (ysonValue) Type() Type {
- return TypeYSON
+func (ysonValue) Type() types.Type {
+ return types.YSON
}
func (v ysonValue) toYDB(a *allocator.Allocator) *Ydb.Value {
@@ -2115,81 +2293,81 @@ func YSONValue(v []byte) ysonValue {
return v
}
-func zeroPrimitiveValue(t PrimitiveType) Value {
+func zeroPrimitiveValue(t types.Primitive) Value {
switch t {
- case TypeBool:
+ case types.Bool:
return BoolValue(false)
- case TypeInt8:
+ case types.Int8:
return Int8Value(0)
- case TypeUint8:
+ case types.Uint8:
return Uint8Value(0)
- case TypeInt16:
+ case types.Int16:
return Int16Value(0)
- case TypeUint16:
+ case types.Uint16:
return Uint16Value(0)
- case TypeInt32:
+ case types.Int32:
return Int32Value(0)
- case TypeUint32:
+ case types.Uint32:
return Uint32Value(0)
- case TypeInt64:
+ case types.Int64:
return Int64Value(0)
- case TypeUint64:
+ case types.Uint64:
return Uint64Value(0)
- case TypeFloat:
+ case types.Float:
return FloatValue(0)
- case TypeDouble:
+ case types.Double:
return DoubleValue(0)
- case TypeDate:
+ case types.Date:
return DateValue(0)
- case TypeDatetime:
+ case types.Datetime:
return DatetimeValue(0)
- case TypeTimestamp:
+ case types.Timestamp:
return TimestampValue(0)
- case TypeInterval:
+ case types.Interval:
return IntervalValue(0)
- case TypeText:
+ case types.Text:
return TextValue("")
- case TypeYSON:
+ case types.YSON:
return YSONValue([]byte(""))
- case TypeJSON:
+ case types.JSON:
return JSONValue("")
- case TypeJSONDocument:
+ case types.JSONDocument:
return JSONDocumentValue("")
- case TypeDyNumber:
+ case types.DyNumber:
return DyNumberValue("")
- case TypeTzDate:
+ case types.TzDate:
return TzDateValue("")
- case TypeTzDatetime:
+ case types.TzDatetime:
return TzDatetimeValue("")
- case TypeTzTimestamp:
+ case types.TzTimestamp:
return TzTimestampValue("")
- case TypeBytes:
+ case types.Bytes:
return BytesValue([]byte{})
- case TypeUUID:
+ case types.UUID:
return UUIDValue([16]byte{})
default:
@@ -2197,53 +2375,55 @@ func zeroPrimitiveValue(t PrimitiveType) Value {
}
}
-func ZeroValue(t Type) Value {
+func ZeroValue(t types.Type) Value {
switch t := t.(type) {
- case PrimitiveType:
+ case types.Primitive:
return zeroPrimitiveValue(t)
- case optionalType:
- return NullValue(t.innerType)
+ case types.Optional:
+ return NullValue(t.InnerType())
- case *voidType:
+ case *types.Void:
return VoidValue()
- case *listType, *emptyListType:
+ case *types.List, *types.EmptyList:
return &listValue{
t: t,
}
- case *setType:
+ case *types.Set:
return &setValue{
t: t,
}
- case *dictType:
+ case *types.Dict:
return &dictValue{
- t: t.valueType,
+ t: t.ValueType(),
}
- case *emptyDictType:
+ case *types.EmptyDict:
return &dictValue{
t: t,
}
- case *TupleType:
+ case *types.Tuple:
return TupleValue(func() []Value {
- values := make([]Value, len(t.items))
- for i, tt := range t.items {
+ innerTypes := t.InnerTypes()
+ values := make([]Value, len(innerTypes))
+ for i, tt := range innerTypes {
values[i] = ZeroValue(tt)
}
return values
}()...)
- case *StructType:
+ case *types.Struct:
return StructValue(func() []StructValueField {
- fields := make([]StructValueField, len(t.fields))
- for i := range t.fields {
- fields[i] = StructValueField{
- Name: t.fields[i].Name,
- V: ZeroValue(t.fields[i].T),
+ fields := t.Fields()
+ values := make([]StructValueField, len(fields))
+ for i := range fields {
+ values[i] = StructValueField{
+ Name: fields[i].Name,
+ V: ZeroValue(fields[i].T),
}
}
- return fields
+ return values
}()...)
case *DecimalType:
return DecimalValue([16]byte{}, decimalPrecision, decimalScale)
@@ -2266,7 +2446,10 @@ func (v bytesValue) castTo(dst interface{}) error {
return nil
default:
- return xerrors.WithStackTrace(fmt.Errorf("cannot cast '%+v' (type '%s') to '%T' destination", v, v.Type().Yql(), vv))
+ return xerrors.WithStackTrace(fmt.Errorf(
+ "%w '%+v' (type '%s') to '%T' destination",
+ ErrCannotCast, v, v.Type().Yql(), vv,
+ ))
}
}
@@ -2274,8 +2457,8 @@ func (v bytesValue) Yql() string {
return fmt.Sprintf("%q", string(v))
}
-func (bytesValue) Type() Type {
- return TypeBytes
+func (bytesValue) Type() types.Type {
+ return types.Bytes
}
func (v bytesValue) toYDB(a *allocator.Allocator) *Ydb.Value {
diff --git a/internal/value/value_test.go b/internal/value/value_test.go
index e8b283fb1..f7adfc7e9 100644
--- a/internal/value/value_test.go
+++ b/internal/value/value_test.go
@@ -1,8 +1,10 @@
package value
import (
+ "fmt"
"math"
"math/big"
+ "reflect"
"strconv"
"testing"
"time"
@@ -11,6 +13,9 @@ import (
"google.golang.org/protobuf/proto"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pg"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
)
func BenchmarkMemory(b *testing.B) {
@@ -68,24 +73,24 @@ func BenchmarkMemory(b *testing.B) {
DictValueField{TextValue("air_date"), Uint64Value(3)},
DictValueField{TextValue("remove_date"), Uint64Value(4)},
),
- NullValue(Optional(Optional(Optional(TypeBool)))),
- VariantValueTuple(Int32Value(42), 1, Tuple(
- TypeBytes,
- TypeInt32,
+ NullValue(types.NewOptional(types.NewOptional(types.NewOptional(types.Bool)))),
+ VariantValueTuple(Int32Value(42), 1, types.NewTuple(
+ types.Bytes,
+ types.Int32,
)),
- VariantValueStruct(Int32Value(42), "bar", Struct(
- StructField{
+ VariantValueStruct(Int32Value(42), "bar", types.NewStruct(
+ types.StructField{
Name: "foo",
- T: TypeBytes,
+ T: types.Bytes,
},
- StructField{
+ types.StructField{
Name: "bar",
- T: TypeInt32,
+ T: types.Int32,
},
)),
- ZeroValue(TypeText),
- ZeroValue(Struct()),
- ZeroValue(Tuple()),
+ ZeroValue(types.Text),
+ ZeroValue(types.NewStruct()),
+ ZeroValue(types.NewTuple()),
)
for i := 0; i < b.N; i++ {
a := allocator.New()
@@ -153,31 +158,32 @@ func TestToYDBFromYDB(t *testing.T) {
DictValueField{TextValue("air_date"), Uint64Value(3)},
DictValueField{TextValue("remove_date"), Uint64Value(4)},
),
- NullValue(TypeBool),
- NullValue(Optional(TypeBool)),
- VariantValueTuple(Int32Value(42), 1, Tuple(
- TypeBytes,
- TypeInt32,
+ NullValue(types.Bool),
+ NullValue(types.NewOptional(types.Bool)),
+ VariantValueTuple(Int32Value(42), 1, types.NewTuple(
+ types.Bytes,
+ types.Int32,
)),
- VariantValueStruct(Int32Value(42), "bar", Struct(
- StructField{
+ VariantValueStruct(Int32Value(42), "bar", types.NewStruct(
+ types.StructField{
Name: "foo",
- T: TypeBytes,
+ T: types.Bytes,
},
- StructField{
+ types.StructField{
Name: "bar",
- T: TypeInt32,
+ T: types.Int32,
},
)),
- ZeroValue(TypeText),
- ZeroValue(Struct()),
- ZeroValue(Tuple()),
+ ZeroValue(types.Text),
+ ZeroValue(types.NewStruct()),
+ ZeroValue(types.NewTuple()),
+ PgValue(pg.OIDInt4, "123"),
} {
t.Run(strconv.Itoa(i)+"."+v.Yql(), func(t *testing.T) {
a := allocator.New()
defer a.Free()
value := ToYDB(v, a)
- dualConversedValue, err := fromYDB(value.Type, value.Value)
+ dualConversedValue, err := fromYDB(value.GetType(), value.GetValue())
require.NoError(t, err)
if !proto.Equal(value, ToYDB(dualConversedValue, a)) {
t.Errorf("dual conversion failed:\n\n - got: %v\n\n - want: %v", ToYDB(dualConversedValue, a), value)
@@ -329,11 +335,11 @@ func TestValueYql(t *testing.T) {
literal: `TzTimestamp("1997-12-14T03:09:42.123456,Europe/Berlin")`,
},
{
- value: NullValue(TypeInt32),
+ value: NullValue(types.Int32),
literal: `Nothing(Optional)`,
},
{
- value: NullValue(Optional(TypeBool)),
+ value: NullValue(types.NewOptional(types.Bool)),
literal: `Nothing(Optional>)`,
},
{
@@ -390,30 +396,36 @@ func TestValueYql(t *testing.T) {
literal: `(0,1l,Float("2"),"3"u)`,
},
{
- value: VariantValueTuple(Int32Value(42), 1, Tuple(
- TypeBytes,
- TypeInt32,
+ value: VariantValueTuple(Int32Value(42), 1, types.NewTuple(
+ types.Bytes,
+ types.Int32,
)),
literal: `Variant(42,"1",Variant)`,
},
{
- value: VariantValueTuple(TextValue("foo"), 1, Tuple(
- TypeBytes,
- TypeText,
+ value: VariantValueTuple(TextValue("foo"), 1, types.NewTuple(
+ types.Bytes,
+ types.Text,
)),
literal: `Variant("foo"u,"1",Variant)`,
},
{
- value: VariantValueTuple(BoolValue(true), 0, Tuple(
- TypeBytes,
- TypeInt32,
+ value: VariantValueTuple(BoolValue(true), 0, types.NewTuple(
+ types.Bytes,
+ types.Int32,
)),
literal: `Variant(true,"0",Variant)`,
},
{
- value: VariantValueStruct(Int32Value(42), "bar", Struct(
- StructField{"foo", TypeBytes},
- StructField{"bar", TypeInt32},
+ value: VariantValueStruct(Int32Value(42), "bar", types.NewStruct(
+ types.StructField{
+ Name: "foo",
+ T: types.Bytes,
+ },
+ types.StructField{
+ Name: "bar",
+ T: types.Int32,
+ },
)),
literal: `Variant(42,"bar",Variant<'bar':Int32,'foo':String>)`,
},
@@ -440,26 +452,32 @@ func TestValueYql(t *testing.T) {
literal: `{"bar"u:Void(),"foo"u:Void()}`,
},
{
- value: ZeroValue(TypeBool),
+ value: ZeroValue(types.Bool),
literal: `false`,
},
{
- value: ZeroValue(Optional(TypeBool)),
+ value: ZeroValue(types.NewOptional(types.Bool)),
literal: `Nothing(Optional)`,
},
{
- value: ZeroValue(Tuple(TypeBool, TypeDouble)),
+ value: ZeroValue(types.NewTuple(types.Bool, types.Double)),
literal: `(false,Double("0"))`,
},
{
- value: ZeroValue(Struct(
- StructField{"foo", TypeBool},
- StructField{"bar", TypeText},
+ value: ZeroValue(types.NewStruct(
+ types.StructField{
+ Name: "foo",
+ T: types.Bool,
+ },
+ types.StructField{
+ Name: "bar",
+ T: types.Text,
+ },
)),
literal: "<|`bar`:\"\"u,`foo`:false|>",
},
{
- value: ZeroValue(TypeUUID),
+ value: ZeroValue(types.UUID),
literal: `Uuid("00000000-0000-0000-0000-000000000000")`,
},
{
@@ -482,9 +500,667 @@ func TestValueYql(t *testing.T) {
value: YSONValue([]byte("[3;%false]")),
literal: `Yson("[3;%false]")`,
},
+ {
+ value: PgValue(pg.OIDUnknown, "123"),
+ literal: `PgConst("123", PgType(705))`,
+ },
} {
t.Run(strconv.Itoa(i)+"."+tt.literal, func(t *testing.T) {
require.Equal(t, tt.literal, tt.value.Yql())
})
}
}
+
+func TestOptionalValueCastTo(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ v *optionalValue
+ dst **string
+ exp interface{}
+ err error
+ }{
+ {
+ name: xtest.CurrentFileLine(),
+ v: OptionalValue(TextValue("test")),
+ dst: func(v *string) **string { return &v }(func(s string) *string { return &s }("")),
+ exp: func(v *string) **string { return &v }(func(s string) *string { return &s }("test")),
+ err: nil,
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ v: OptionalValue(TextValue("test")),
+ dst: func(v *string) **string { return &v }(func() *string { return nil }()),
+ exp: func(v *string) **string { return &v }(func(s string) *string { return &s }("test")),
+ err: nil,
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ v: NullValue(types.Text),
+ dst: func(v *string) **string { return &v }(func(s string) *string { return &s }("")),
+ exp: func(v *string) **string { return &v }(func() *string { return nil }()),
+ err: nil,
+ },
+ {
+ name: xtest.CurrentFileLine(),
+ v: NullValue(types.Text),
+ dst: func(v *string) **string { return &v }(func() *string { return nil }()),
+ exp: func(v *string) **string { return &v }(func() *string { return nil }()),
+ err: nil,
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ err := tt.v.castTo(tt.dst)
+ if tt.err != nil {
+ require.ErrorIs(t, err, tt.err)
+ } else {
+ require.NoError(t, err)
+ require.Equal(t, tt.exp, tt.dst)
+ }
+ })
+ }
+}
+
+func TestNullable(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ t types.Type
+ v interface{}
+ exp Value
+ }{
+ {
+ name: "bool",
+ t: types.Bool,
+ v: func(v bool) *bool { return &v }(true),
+ exp: OptionalValue(BoolValue(true)),
+ },
+ {
+ name: "nil bool",
+ t: types.Bool,
+ v: func() *bool { return nil }(),
+ exp: NullValue(types.Bool),
+ },
+ {
+ name: "int8",
+ t: types.Int8,
+ v: func(v int8) *int8 { return &v }(123),
+ exp: OptionalValue(Int8Value(123)),
+ },
+ {
+ name: "nil int8",
+ t: types.Int8,
+ v: func() *int8 { return nil }(),
+ exp: NullValue(types.Int8),
+ },
+ {
+ name: "uint8",
+ t: types.Uint8,
+ v: func(v uint8) *uint8 { return &v }(123),
+ exp: OptionalValue(Uint8Value(123)),
+ },
+ {
+ name: "nil uint8",
+ t: types.Uint8,
+ v: func() *uint8 { return nil }(),
+ exp: NullValue(types.Uint8),
+ },
+ {
+ name: "int16",
+ t: types.Int16,
+ v: func(v int16) *int16 { return &v }(123),
+ exp: OptionalValue(Int16Value(123)),
+ },
+ {
+ name: "nil int16",
+ t: types.Int16,
+ v: func() *int16 { return nil }(),
+ exp: NullValue(types.Int16),
+ },
+ {
+ name: "uint16",
+ t: types.Uint16,
+ v: func(v uint16) *uint16 { return &v }(123),
+ exp: OptionalValue(Uint16Value(123)),
+ },
+ {
+ name: "nil uint16",
+ t: types.Uint16,
+ v: func() *uint16 { return nil }(),
+ exp: NullValue(types.Uint16),
+ },
+ {
+ name: "int32",
+ t: types.Int32,
+ v: func(v int32) *int32 { return &v }(123),
+ exp: OptionalValue(Int32Value(123)),
+ },
+ {
+ name: "nil int32",
+ t: types.Int32,
+ v: func() *int32 { return nil }(),
+ exp: NullValue(types.Int32),
+ },
+ {
+ name: "uint32",
+ t: types.Uint32,
+ v: func(v uint32) *uint32 { return &v }(123),
+ exp: OptionalValue(Uint32Value(123)),
+ },
+ {
+ name: "nil uint32",
+ t: types.Uint32,
+ v: func() *uint32 { return nil }(),
+ exp: NullValue(types.Uint32),
+ },
+ {
+ name: "int64",
+ t: types.Int64,
+ v: func(v int64) *int64 { return &v }(123),
+ exp: OptionalValue(Int64Value(123)),
+ },
+ {
+ name: "nil int64",
+ t: types.Int64,
+ v: func() *int64 { return nil }(),
+ exp: NullValue(types.Int64),
+ },
+ {
+ name: "uint64",
+ t: types.Uint64,
+ v: func(v uint64) *uint64 { return &v }(123),
+ exp: OptionalValue(Uint64Value(123)),
+ },
+ {
+ name: "nil uint64",
+ t: types.Uint64,
+ v: func() *uint64 { return nil }(),
+ exp: NullValue(types.Uint64),
+ },
+ {
+ name: "float",
+ t: types.Float,
+ v: func(v float32) *float32 { return &v }(123),
+ exp: OptionalValue(FloatValue(123)),
+ },
+ {
+ name: "nil float",
+ t: types.Float,
+ v: func() *float32 { return nil }(),
+ exp: NullValue(types.Float),
+ },
+ {
+ name: "double",
+ t: types.Double,
+ v: func(v float64) *float64 { return &v }(123),
+ exp: OptionalValue(DoubleValue(123)),
+ },
+ {
+ name: "nil float",
+ t: types.Double,
+ v: func() *float64 { return nil }(),
+ exp: NullValue(types.Double),
+ },
+ {
+ name: "date from int32",
+ t: types.Date,
+ v: func(v uint32) *uint32 { return &v }(123),
+ exp: OptionalValue(DateValue(123)),
+ },
+ {
+ name: "date from time.Time",
+ t: types.Date,
+ v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
+ exp: OptionalValue(DateValueFromTime(time.Unix(123, 456))),
+ },
+ {
+ name: "nil date",
+ t: types.Date,
+ v: func() *uint32 { return nil }(),
+ exp: NullValue(types.Date),
+ },
+ {
+ name: "datetime from int32",
+ t: types.Datetime,
+ v: func(v uint32) *uint32 { return &v }(123),
+ exp: OptionalValue(DatetimeValue(123)),
+ },
+ {
+ name: "datetime from time.Time",
+ t: types.Datetime,
+ v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
+ exp: OptionalValue(DatetimeValueFromTime(time.Unix(123, 456))),
+ },
+ {
+ name: "nil datetime",
+ t: types.Datetime,
+ v: func() *uint32 { return nil }(),
+ exp: NullValue(types.Datetime),
+ },
+ {
+ name: "timestamp from int32",
+ t: types.Timestamp,
+ v: func(v uint64) *uint64 { return &v }(123),
+ exp: OptionalValue(TimestampValue(123)),
+ },
+ {
+ name: "timestamp from time.Time",
+ t: types.Timestamp,
+ v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
+ exp: OptionalValue(TimestampValueFromTime(time.Unix(123, 456))),
+ },
+ {
+ name: "nil timestamp",
+ t: types.Timestamp,
+ v: func() *uint64 { return nil }(),
+ exp: NullValue(types.Timestamp),
+ },
+ {
+ name: "tzDate from int32",
+ t: types.TzDate,
+ v: func(v string) *string { return &v }(""),
+ exp: OptionalValue(TzDateValue("")),
+ },
+ {
+ name: "tzDate from time.Time",
+ t: types.TzDate,
+ v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
+ exp: OptionalValue(TzDateValueFromTime(time.Unix(123, 456))),
+ },
+ {
+ name: "nil tzDate",
+ t: types.TzDate,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.TzDate),
+ },
+ {
+ name: "interval from int64",
+ t: types.Interval,
+ v: func(v int64) *int64 { return &v }(123),
+ exp: OptionalValue(IntervalValue(123)),
+ },
+ {
+ name: "interval from time.Time",
+ t: types.Interval,
+ v: func(v time.Duration) *time.Duration { return &v }(time.Second),
+ exp: OptionalValue(IntervalValueFromDuration(time.Second)),
+ },
+ {
+ name: "nil interval",
+ t: types.Interval,
+ v: func() *int64 { return nil }(),
+ exp: NullValue(types.Interval),
+ },
+ {
+ name: "tzDatetime from int32",
+ t: types.TzDatetime,
+ v: func(v string) *string { return &v }(""),
+ exp: OptionalValue(TzDatetimeValue("")),
+ },
+ {
+ name: "tzTzDatetime from time.Time",
+ t: types.TzDatetime,
+ v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
+ exp: OptionalValue(TzDatetimeValueFromTime(time.Unix(123, 456))),
+ },
+ {
+ name: "nil tzTzDatetime",
+ t: types.TzDatetime,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.TzDatetime),
+ },
+ {
+ name: "tzTimestamp from int32",
+ t: types.TzTimestamp,
+ v: func(v string) *string { return &v }(""),
+ exp: OptionalValue(TzTimestampValue("")),
+ },
+ {
+ name: "TzTimestamp from time.Time",
+ t: types.TzTimestamp,
+ v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
+ exp: OptionalValue(TzTimestampValueFromTime(time.Unix(123, 456))),
+ },
+ {
+ name: "nil TzTimestamp",
+ t: types.TzTimestamp,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.TzTimestamp),
+ },
+ {
+ name: "string",
+ t: types.Bytes,
+ v: func(v string) *string { return &v }("test"),
+ exp: OptionalValue(BytesValue([]byte("test"))),
+ },
+ {
+ name: "string",
+ t: types.Bytes,
+ v: func(v []byte) *[]byte { return &v }([]byte("test")),
+ exp: OptionalValue(BytesValue([]byte("test"))),
+ },
+ {
+ name: "nil string",
+ t: types.Bytes,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.Bytes),
+ },
+ {
+ name: "utf8",
+ t: types.Text,
+ v: func(v string) *string { return &v }("test"),
+ exp: OptionalValue(TextValue("test")),
+ },
+ {
+ name: "nil utf8",
+ t: types.Text,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.Text),
+ },
+ {
+ name: "yson",
+ t: types.YSON,
+ v: func(v string) *string { return &v }("test"),
+ exp: OptionalValue(YSONValue([]byte("test"))),
+ },
+ {
+ name: "yson",
+ t: types.YSON,
+ v: func(v []byte) *[]byte { return &v }([]byte("test")),
+ exp: OptionalValue(YSONValue([]byte("test"))),
+ },
+ {
+ name: "nil yson",
+ t: types.YSON,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.YSON),
+ },
+ {
+ name: "json",
+ t: types.JSON,
+ v: func(v string) *string { return &v }("test"),
+ exp: OptionalValue(JSONValue("test")),
+ },
+ {
+ name: "json",
+ t: types.JSON,
+ v: func(v []byte) *[]byte { return &v }([]byte("test")),
+ exp: OptionalValue(JSONValue("test")),
+ },
+ {
+ name: "nil json",
+ t: types.JSON,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.JSON),
+ },
+ {
+ name: "uuid",
+ t: types.UUID,
+ v: func(v [16]byte) *[16]byte { return &v }([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
+ exp: OptionalValue(UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})),
+ },
+ {
+ name: "jsonDocument",
+ t: types.JSONDocument,
+ v: func(v string) *string { return &v }("test"),
+ exp: OptionalValue(JSONDocumentValue("test")),
+ },
+ {
+ name: "jsonDocument",
+ t: types.JSONDocument,
+ v: func(v []byte) *[]byte { return &v }([]byte("test")),
+ exp: OptionalValue(JSONDocumentValue("test")),
+ },
+ {
+ name: "nil jsonDocument",
+ t: types.JSONDocument,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.JSONDocument),
+ },
+ {
+ name: "dyNumber",
+ t: types.DyNumber,
+ v: func(v string) *string { return &v }("test"),
+ exp: OptionalValue(DyNumberValue("test")),
+ },
+ {
+ name: "nil dyNumber",
+ t: types.DyNumber,
+ v: func() *string { return nil }(),
+ exp: NullValue(types.DyNumber),
+ },
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ a := allocator.New()
+ defer a.Free()
+ v := Nullable(test.t, test.v)
+ if !proto.Equal(ToYDB(v, a), ToYDB(test.exp, a)) {
+ t.Fatalf("unexpected value: %v, exp: %v", v, test.exp)
+ }
+ })
+ }
+}
+
+func TestCastNumbers(t *testing.T) {
+ numberValues := []struct {
+ value Value
+ signed bool
+ len int
+ }{
+ {
+ value: Uint64Value(1),
+ signed: false,
+ len: 8,
+ },
+ {
+ value: Int64Value(2),
+ signed: true,
+ len: 8,
+ },
+ {
+ value: Uint32Value(3),
+ signed: false,
+ len: 4,
+ },
+ {
+ value: Int32Value(4),
+ signed: true,
+ len: 4,
+ },
+ {
+ value: Uint16Value(5),
+ signed: false,
+ len: 2,
+ },
+ {
+ value: Int16Value(6),
+ signed: true,
+ len: 2,
+ },
+ {
+ value: Uint8Value(7),
+ signed: false,
+ len: 1,
+ },
+ {
+ value: Int8Value(8),
+ signed: true,
+ len: 1,
+ },
+ }
+ numberDestinations := []struct {
+ destination interface{}
+ signed bool
+ len int
+ }{
+ {
+ destination: func(v uint64) *uint64 { return &v }(1),
+ signed: false,
+ len: 8,
+ },
+ {
+ destination: func(v int64) *int64 { return &v }(2),
+ signed: true,
+ len: 8,
+ },
+ {
+ destination: func(v uint32) *uint32 { return &v }(3),
+ signed: false,
+ len: 4,
+ },
+ {
+ destination: func(v int32) *int32 { return &v }(4),
+ signed: true,
+ len: 4,
+ },
+ {
+ destination: func(v uint16) *uint16 { return &v }(5),
+ signed: false,
+ len: 2,
+ },
+ {
+ destination: func(v int16) *int16 { return &v }(6),
+ signed: true,
+ len: 2,
+ },
+ {
+ destination: func(v uint8) *uint8 { return &v }(7),
+ signed: false,
+ len: 1,
+ },
+ {
+ destination: func(v int8) *int8 { return &v }(8),
+ signed: true,
+ len: 1,
+ },
+ {
+ destination: func(v float32) *float32 { return &v }(7),
+ signed: true,
+ len: 4,
+ },
+ {
+ destination: func(v float64) *float64 { return &v }(8),
+ signed: true,
+ len: 8,
+ },
+ }
+ for _, dst := range numberDestinations {
+ for _, src := range numberValues {
+ t.Run(fmt.Sprintf("%s→%s",
+ src.value.Yql(), reflect.ValueOf(dst.destination).Type().Elem().String(),
+ ), func(t *testing.T) {
+ mustErr := false
+ switch {
+ case src.len == dst.len && src.signed != dst.signed,
+ src.len > dst.len,
+ src.signed && !dst.signed:
+ mustErr = true
+ }
+ err := CastTo(src.value, dst.destination)
+ if mustErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ t.Run(fmt.Sprintf("Optional(%s)→%s",
+ src.value.Yql(), reflect.ValueOf(dst.destination).Type().Elem().String(),
+ ), func(t *testing.T) {
+ mustErr := false
+ switch {
+ case src.len == dst.len && src.signed != dst.signed,
+ src.len > dst.len,
+ src.signed && !dst.signed:
+ mustErr = true
+ }
+ err := CastTo(OptionalValue(src.value), dst.destination)
+ if mustErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+ })
+ }
+ }
+}
+
+func TestCastOtherTypes(t *testing.T) {
+ for _, tt := range []struct {
+ v Value
+ dst interface{}
+ result interface{}
+ error bool
+ }{
+ {
+ v: BytesValue([]byte("test")),
+ dst: func(v []byte) *[]byte { return &v }(make([]byte, 0, 10)),
+ result: func(v []byte) *[]byte { return &v }([]byte("test")),
+ error: false,
+ },
+ {
+ v: TextValue("test"),
+ dst: func(v []byte) *[]byte { return &v }(make([]byte, 0, 10)),
+ result: func(v []byte) *[]byte { return &v }([]byte("test")),
+ error: false,
+ },
+ {
+ v: BytesValue([]byte("test")),
+ dst: func(v string) *string { return &v }(""),
+ result: func(v string) *string { return &v }("test"),
+ error: false,
+ },
+ {
+ v: DoubleValue(123),
+ dst: func(v float64) *float64 { return &v }(9),
+ result: func(v float64) *float64 { return &v }(123),
+ error: false,
+ },
+ {
+ v: DoubleValue(123),
+ dst: func(v float32) *float32 { return &v }(9),
+ result: func(v float32) *float32 { return &v }(9),
+ error: true,
+ },
+ {
+ v: FloatValue(123),
+ dst: func(v float64) *float64 { return &v }(9),
+ result: func(v float64) *float64 { return &v }(123),
+ error: false,
+ },
+ {
+ v: FloatValue(123),
+ dst: func(v float32) *float32 { return &v }(9),
+ result: func(v float32) *float32 { return &v }(123),
+ error: false,
+ },
+ {
+ v: Uint64Value(123),
+ dst: func(v float32) *float32 { return &v }(9),
+ result: func(v float32) *float32 { return &v }(9),
+ error: true,
+ },
+ {
+ v: Uint64Value(123),
+ dst: func(v float64) *float64 { return &v }(9),
+ result: func(v float64) *float64 { return &v }(9),
+ error: true,
+ },
+ {
+ v: OptionalValue(DoubleValue(123)),
+ dst: func(v float64) *float64 { return &v }(9),
+ result: func(v float64) *float64 { return &v }(123),
+ error: false,
+ },
+ } {
+ t.Run(fmt.Sprintf("%s→%v", tt.v.Type().Yql(), reflect.ValueOf(tt.dst).Type().Elem()),
+ func(t *testing.T) {
+ if err := CastTo(tt.v, tt.dst); (err != nil) != tt.error {
+ t.Errorf("castTo() error = %v, want %v", err, tt.error)
+ } else if !reflect.DeepEqual(tt.dst, tt.result) {
+ t.Errorf("castTo() result = %+v, want %+v",
+ reflect.ValueOf(tt.dst).Elem(),
+ reflect.ValueOf(tt.result).Elem(),
+ )
+ }
+ },
+ )
+ }
+}
diff --git a/internal/version/version.go b/internal/version/version.go
index 72df04221..a20971e2f 100644
--- a/internal/version/version.go
+++ b/internal/version/version.go
@@ -2,8 +2,8 @@ package version
const (
Major = "3"
- Minor = "56"
- Patch = "1"
+ Minor = "61"
+ Patch = "2"
Prefix = "ydb-go-sdk"
)
diff --git a/internal/xcontext/context_error.go b/internal/xcontext/context_error.go
index ddd11553c..419ec12c9 100644
--- a/internal/xcontext/context_error.go
+++ b/internal/xcontext/context_error.go
@@ -4,7 +4,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
)
-var _ error = (*ctxErr)(nil)
+var _ error = (*ctxError)(nil)
const (
atWord = "at"
@@ -12,7 +12,7 @@ const (
)
func errAt(err error, skipDepth int) error {
- return &ctxErr{
+ return &ctxError{
err: err,
stackRecord: stack.Record(skipDepth + 1),
linkWord: atWord,
@@ -20,23 +20,23 @@ func errAt(err error, skipDepth int) error {
}
func errFrom(err error, from string) error {
- return &ctxErr{
+ return &ctxError{
err: err,
stackRecord: from,
linkWord: fromWord,
}
}
-type ctxErr struct {
+type ctxError struct {
err error
stackRecord string
linkWord string
}
-func (e *ctxErr) Error() string {
+func (e *ctxError) Error() string {
return "'" + e.err.Error() + "' " + e.linkWord + " `" + e.stackRecord + "`"
}
-func (e *ctxErr) Unwrap() error {
+func (e *ctxError) Unwrap() error {
return e.err
}
diff --git a/internal/xcontext/without_deadline.go b/internal/xcontext/without_deadline.go
index c0d92045c..2e80a284f 100644
--- a/internal/xcontext/without_deadline.go
+++ b/internal/xcontext/without_deadline.go
@@ -13,7 +13,7 @@ func (valueOnlyContext) Done() <-chan struct{} { return nil }
func (valueOnlyContext) Err() error { return nil }
-// WithoutDeadline helps to clear derived deadline from deadline
-func WithoutDeadline(ctx context.Context) context.Context {
+// ValueOnly helps to clear parent context from deadlines/cancels
+func ValueOnly(ctx context.Context) context.Context {
return valueOnlyContext{ctx}
}
diff --git a/internal/xerrors/check.go b/internal/xerrors/check.go
index 47b2b1603..2c6895f6b 100644
--- a/internal/xerrors/check.go
+++ b/internal/xerrors/check.go
@@ -9,7 +9,7 @@ func Check(err error) (
code int64,
errType Type,
backoffType backoff.Type,
- deleteSession bool,
+ invalidObject bool,
) {
if err == nil {
return -1,
@@ -19,7 +19,7 @@ func Check(err error) (
}
var e Error
if As(err, &e) {
- return int64(e.Code()), e.Type(), e.BackoffType(), e.MustDeleteSession()
+ return int64(e.Code()), e.Type(), e.BackoffType(), e.IsRetryObjectValid()
}
return -1,
@@ -28,11 +28,15 @@ func Check(err error) (
false
}
-func MustDeleteSession(err error) bool {
+func IsRetryObjectValid(err error) bool {
+ if err == nil {
+ return true
+ }
+
var e Error
if As(err, &e) {
- return e.MustDeleteSession()
+ return !e.IsRetryObjectValid()
}
- return false
+ return true
}
diff --git a/internal/xerrors/issues.go b/internal/xerrors/issues.go
index f981207b4..066bb94eb 100644
--- a/internal/xerrors/issues.go
+++ b/internal/xerrors/issues.go
@@ -44,9 +44,9 @@ func (ii issues) String() string {
b.WriteByte('\'')
b.WriteString(strings.TrimSuffix(m.GetMessage(), "."))
b.WriteByte('\'')
- if len(m.Issues) > 0 {
+ if len(m.GetIssues()) > 0 {
b.WriteByte(' ')
- b.WriteString(issues(m.Issues).String())
+ b.WriteString(issues(m.GetIssues()).String())
}
b.WriteByte('}')
}
@@ -57,7 +57,7 @@ func (ii issues) String() string {
// NewWithIssues returns error which contains child issues
func NewWithIssues(text string, issues ...error) error {
- err := &errorWithIssues{
+ err := &withIssuesError{
reason: text,
}
for i := range issues {
@@ -69,14 +69,14 @@ func NewWithIssues(text string, issues ...error) error {
return err
}
-type errorWithIssues struct {
+type withIssuesError struct {
reason string
issues []error
}
-func (e *errorWithIssues) isYdbError() {}
+func (e *withIssuesError) isYdbError() {}
-func (e *errorWithIssues) Error() string {
+func (e *withIssuesError) Error() string {
var b bytes.Buffer
if len(e.reason) > 0 {
b.WriteString(e.reason)
@@ -95,7 +95,7 @@ func (e *errorWithIssues) Error() string {
return b.String()
}
-func (e *errorWithIssues) As(target interface{}) bool {
+func (e *withIssuesError) As(target interface{}) bool {
for _, err := range e.issues {
if As(err, target) {
return true
@@ -105,7 +105,7 @@ func (e *errorWithIssues) As(target interface{}) bool {
return false
}
-func (e *errorWithIssues) Is(target error) bool {
+func (e *withIssuesError) Is(target error) bool {
for _, err := range e.issues {
if Is(err, target) {
return true
@@ -130,7 +130,7 @@ func (it IssueIterator) Len() int {
func (it IssueIterator) Get(i int) (issue Issue, nested IssueIterator) {
x := it[i]
- if xs := x.Issues; len(xs) > 0 {
+ if xs := x.GetIssues(); len(xs) > 0 {
nested = IssueIterator(xs)
}
diff --git a/internal/xerrors/join.go b/internal/xerrors/join.go
index 1f2e37887..80ec421c1 100644
--- a/internal/xerrors/join.go
+++ b/internal/xerrors/join.go
@@ -7,16 +7,20 @@ import (
)
func Join(errs ...error) joinError {
- return errs
+ return joinError{
+ errs: errs,
+ }
}
-type joinError []error
+type joinError struct {
+ errs []error
+}
-func (errs joinError) Error() string {
+func (e joinError) Error() string {
b := xstring.Buffer()
defer b.Free()
b.WriteByte('[')
- for i, err := range errs {
+ for i, err := range e.errs {
if i > 0 {
_ = b.WriteByte(',')
}
@@ -27,8 +31,8 @@ func (errs joinError) Error() string {
return b.String()
}
-func (errs joinError) As(target interface{}) bool {
- for _, err := range errs {
+func (e joinError) As(target interface{}) bool {
+ for _, err := range e.errs {
if As(err, target) {
return true
}
@@ -37,8 +41,8 @@ func (errs joinError) As(target interface{}) bool {
return false
}
-func (errs joinError) Is(target error) bool {
- for _, err := range errs {
+func (e joinError) Is(target error) bool {
+ for _, err := range e.errs {
if Is(err, target) {
return true
}
@@ -47,6 +51,6 @@ func (errs joinError) Is(target error) bool {
return false
}
-func (errs joinError) Unwrap() []error {
- return errs
+func (e joinError) Unwrap() []error {
+ return e.errs
}
diff --git a/internal/xerrors/operation.go b/internal/xerrors/operation.go
index ded028e9f..7020e4f42 100644
--- a/internal/xerrors/operation.go
+++ b/internal/xerrors/operation.go
@@ -192,7 +192,7 @@ func (e *operationError) BackoffType() backoff.Type {
}
}
-func (e *operationError) MustDeleteSession() bool {
+func (e *operationError) IsRetryObjectValid() bool {
switch e.code {
case
Ydb.StatusIds_BAD_SESSION,
diff --git a/internal/xerrors/retryable.go b/internal/xerrors/retryable.go
index f37bc2eb6..1c3141e8f 100644
--- a/internal/xerrors/retryable.go
+++ b/internal/xerrors/retryable.go
@@ -7,11 +7,11 @@ import (
)
type retryableError struct {
- name string
- err error
- backoffType backoff.Type
- mustDeleteSession bool
- code int32
+ name string
+ err error
+ backoffType backoff.Type
+ isRetryObjectValid bool
+ code int32
}
func (e *retryableError) Code() int32 {
@@ -30,8 +30,8 @@ func (e *retryableError) BackoffType() backoff.Type {
return e.backoffType
}
-func (e *retryableError) MustDeleteSession() bool {
- return e.mustDeleteSession
+func (e *retryableError) IsRetryObjectValid() bool {
+ return e.isRetryObjectValid
}
func (e *retryableError) Error() string {
@@ -56,9 +56,9 @@ func WithName(name string) RetryableErrorOption {
}
}
-func WithDeleteSession() RetryableErrorOption {
+func InvalidObject() RetryableErrorOption {
return func(e *retryableError) {
- e.mustDeleteSession = true
+ e.isRetryObjectValid = true
}
}
@@ -66,20 +66,21 @@ func Retryable(err error, opts ...RetryableErrorOption) error {
var (
e Error
re = &retryableError{
- err: err,
- name: "CUSTOM",
- code: -1,
+ err: err,
+ name: "CUSTOM",
+ code: -1,
+ isRetryObjectValid: true,
}
)
if As(err, &e) {
re.backoffType = e.BackoffType()
- re.mustDeleteSession = e.MustDeleteSession()
+ re.isRetryObjectValid = e.IsRetryObjectValid()
re.code = e.Code()
re.name = e.Name()
}
- for _, o := range opts {
- if o != nil {
- o(re)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(re)
}
}
diff --git a/internal/xerrors/stacktrace.go b/internal/xerrors/stacktrace.go
index d59c092e7..3140547d5 100644
--- a/internal/xerrors/stacktrace.go
+++ b/internal/xerrors/stacktrace.go
@@ -24,9 +24,9 @@ func WithStackTrace(err error, opts ...withStackTraceOption) error {
return nil
}
options := withStackTraceOptions{}
- for _, o := range opts {
- if o != nil {
- o(&options)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&options)
}
}
if s, has := grpcStatus.FromError(err); has {
diff --git a/internal/xerrors/transport.go b/internal/xerrors/transport.go
index ad2bf00a6..b66f7735c 100644
--- a/internal/xerrors/transport.go
+++ b/internal/xerrors/transport.go
@@ -98,7 +98,7 @@ func (e *transportError) BackoffType() backoff.Type {
}
}
-func (e *transportError) MustDeleteSession() bool {
+func (e *transportError) IsRetryObjectValid() bool {
switch e.status.Code() {
case
grpcCodes.ResourceExhausted,
@@ -141,7 +141,7 @@ func Transport(err error, opts ...teOpt) error {
}
var te *transportError
if errors.As(err, &te) {
- return err
+ return te
}
if s, ok := grpcStatus.FromError(err); ok {
te = &transportError{
diff --git a/internal/xerrors/xerrors.go b/internal/xerrors/xerrors.go
index a15bf85f8..ce6a20260 100644
--- a/internal/xerrors/xerrors.go
+++ b/internal/xerrors/xerrors.go
@@ -19,7 +19,7 @@ type Error interface {
Name() string
Type() Type
BackoffType() backoff.Type
- MustDeleteSession() bool
+ IsRetryObjectValid() bool
}
func IsTimeoutError(err error) bool {
@@ -99,3 +99,7 @@ func Is(err error, targets ...error) bool {
return false
}
+
+func IsContextError(err error) bool {
+ return Is(err, context.Canceled, context.DeadlineExceeded)
+}
diff --git a/internal/xrand/xrand.go b/internal/xrand/xrand.go
index 10c974ef0..2f8ed0021 100644
--- a/internal/xrand/xrand.go
+++ b/internal/xrand/xrand.go
@@ -35,9 +35,9 @@ func New(opts ...option) Rand {
r := &r{
r: rand.New(rand.NewSource(time.Now().Unix())), //nolint:gosec
}
- for _, o := range opts {
- if o != nil {
- o(r)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(r)
}
}
diff --git a/internal/xresolver/xresolver.go b/internal/xresolver/xresolver.go
index 5d78a7a55..7eb2046a1 100644
--- a/internal/xresolver/xresolver.go
+++ b/internal/xresolver/xresolver.go
@@ -33,7 +33,7 @@ func (c *clientConn) Endpoint() string {
func (c *clientConn) UpdateState(state resolver.State) (err error) {
onDone := trace.DriverOnResolve(c.trace,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xresolver.(*clientConn).UpdateState"),
c.Endpoint(), func() (addrs []string) {
for i := range state.Addresses {
addrs = append(addrs, state.Addresses[i].Addr)
diff --git a/internal/xsql/badconn/badconn.go b/internal/xsql/badconn/badconn.go
index 0b91649ec..54f5deb8c 100644
--- a/internal/xsql/badconn/badconn.go
+++ b/internal/xsql/badconn/badconn.go
@@ -43,7 +43,7 @@ func Map(err error) error {
return nil
case xerrors.Is(err, io.EOF):
return io.EOF
- case xerrors.MustDeleteSession(err):
+ case !xerrors.IsRetryObjectValid(err):
return Error{err: err}
default:
return err
diff --git a/internal/xsql/badconn/badconn_test.go b/internal/xsql/badconn/badconn_test.go
index 1d252c051..7366542e2 100644
--- a/internal/xsql/badconn/badconn_test.go
+++ b/internal/xsql/badconn/badconn_test.go
@@ -45,12 +45,12 @@ var errsToCheck = []error{
xerrors.Retryable(
xerrors.Transport(grpcStatus.Error(grpcCodes.Unavailable, "")),
xerrors.WithBackoff(backoff.TypeFast),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
),
xerrors.Retryable(
grpcStatus.Error(grpcCodes.Unavailable, ""),
xerrors.WithBackoff(backoff.TypeFast),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
),
xerrors.Transport(grpcStatus.Error(grpcCodes.DataLoss, "")),
xerrors.Transport(grpcStatus.Error(grpcCodes.Unauthenticated, "")),
@@ -112,7 +112,7 @@ var errsToCheck = []error{
xerrors.WithStatusCode(Ydb.StatusIds_SESSION_BUSY),
),
xerrors.Retryable(errors.New("retryable error")),
- xerrors.Retryable(errors.New("retryable error"), xerrors.WithDeleteSession()),
+ xerrors.Retryable(errors.New("retryable error"), xerrors.InvalidObject()),
io.EOF,
xerrors.WithStackTrace(io.EOF),
}
@@ -122,7 +122,7 @@ func Test_badConnError_Is(t *testing.T) {
t.Run(err.Error(), func(t *testing.T) {
err = Map(err)
require.Equal(t,
- xerrors.MustDeleteSession(err),
+ !xerrors.IsRetryObjectValid(err),
xerrors.Is(err, driver.ErrBadConn),
)
})
diff --git a/internal/xsql/conn.go b/internal/xsql/conn.go
index 5dd13743d..c5d670517 100644
--- a/internal/xsql/conn.go
+++ b/internal/xsql/conn.go
@@ -11,6 +11,7 @@ import (
"sync/atomic"
"time"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme/helpers"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
@@ -135,9 +136,9 @@ func newConn(ctx context.Context, c *Connector, s table.ClosableSession, opts ..
cc.beginTxFuncs = map[QueryMode]beginTxFunc{
DataQueryMode: cc.beginTx,
}
- for _, o := range opts {
- if o != nil {
- o(cc)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(cc)
}
}
c.attach(cc)
@@ -154,7 +155,7 @@ func (c *conn) PrepareContext(ctx context.Context, query string) (_ driver.Stmt,
return c.currentTx.PrepareContext(ctx, query)
}
onDone := trace.DatabaseSQLOnConnPrepare(c.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).PrepareContext"),
query,
)
defer func() {
@@ -197,7 +198,7 @@ func (c *conn) execContext(ctx context.Context, query string, args []driver.Name
m = queryModeFromContext(ctx, c.defaultQueryMode)
onDone = trace.DatabaseSQLOnConnExec(
c.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).execContext"),
query, m.String(), xcontext.IsIdempotent(ctx), c.sinceLastUsage(),
)
)
@@ -207,13 +208,13 @@ func (c *conn) execContext(ctx context.Context, query string, args []driver.Name
switch m {
case DataQueryMode:
- normalizedQuery, params, err := c.normalize(query, args...)
+ normalizedQuery, parameters, err := c.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
_, res, err := c.session.Execute(ctx,
txControl(ctx, c.defaultTxControl),
- normalizedQuery, params, c.dataQueryOptions(ctx)...,
+ normalizedQuery, ¶meters, c.dataQueryOptions(ctx)...,
)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
@@ -241,15 +242,12 @@ func (c *conn) execContext(ctx context.Context, query string, args []driver.Name
return resultNoRows{}, nil
case ScriptingQueryMode:
- var (
- res result.StreamResult
- params *table.QueryParameters
- )
- normalizedQuery, params, err := c.normalize(query, args...)
+ var res result.StreamResult
+ normalizedQuery, parameters, err := c.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
- res, err = c.connector.parent.Scripting().StreamExecute(ctx, normalizedQuery, params)
+ res, err = c.connector.parent.Scripting().StreamExecute(ctx, normalizedQuery, ¶meters)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
}
@@ -310,7 +308,7 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam
m = queryModeFromContext(ctx, c.defaultQueryMode)
onDone = trace.DatabaseSQLOnConnQuery(
c.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).queryContext"),
query, m.String(), xcontext.IsIdempotent(ctx), c.sinceLastUsage(),
)
)
@@ -320,13 +318,13 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam
switch m {
case DataQueryMode:
- normalizedQuery, params, err := c.normalize(query, args...)
+ normalizedQuery, parameters, err := c.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
_, res, err := c.session.Execute(ctx,
txControl(ctx, c.defaultTxControl),
- normalizedQuery, params, c.dataQueryOptions(ctx)...,
+ normalizedQuery, ¶meters, c.dataQueryOptions(ctx)...,
)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
@@ -340,12 +338,12 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam
result: res,
}, nil
case ScanQueryMode:
- normalizedQuery, params, err := c.normalize(query, args...)
+ normalizedQuery, parameters, err := c.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
res, err := c.session.StreamExecuteScanQuery(ctx,
- normalizedQuery, params, c.scanQueryOptions(ctx)...,
+ normalizedQuery, ¶meters, c.scanQueryOptions(ctx)...,
)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
@@ -375,11 +373,11 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam
},
}, nil
case ScriptingQueryMode:
- normalizedQuery, params, err := c.normalize(query, args...)
+ normalizedQuery, parameters, err := c.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
- res, err := c.connector.parent.Scripting().StreamExecute(ctx, normalizedQuery, params)
+ res, err := c.connector.parent.Scripting().StreamExecute(ctx, normalizedQuery, ¶meters)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
}
@@ -397,7 +395,9 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam
}
func (c *conn) Ping(ctx context.Context) (finalErr error) {
- onDone := trace.DatabaseSQLOnConnPing(c.trace, &ctx, stack.FunctionID(""))
+ onDone := trace.DatabaseSQLOnConnPing(c.trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).Ping"),
+ )
defer func() {
onDone(finalErr)
}()
@@ -416,7 +416,7 @@ func (c *conn) Close() (finalErr error) {
c.connector.detach(c)
onDone := trace.DatabaseSQLOnConnClose(
c.trace, &c.openConnCtx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).Close"),
)
defer func() {
onDone(finalErr)
@@ -424,7 +424,7 @@ func (c *conn) Close() (finalErr error) {
if c.currentTx != nil {
_ = c.currentTx.Rollback()
}
- err := c.session.Close(xcontext.WithoutDeadline(c.openConnCtx))
+ err := c.session.Close(xcontext.ValueOnly(c.openConnCtx))
if err != nil {
return badconn.Map(xerrors.WithStackTrace(err))
}
@@ -443,7 +443,7 @@ func (c *conn) Begin() (driver.Tx, error) {
return nil, errDeprecated
}
-func (c *conn) normalize(q string, args ...driver.NamedValue) (query string, _ *table.QueryParameters, _ error) {
+func (c *conn) normalize(q string, args ...driver.NamedValue) (query string, _ params.Parameters, _ error) {
return c.connector.Bindings.RewriteQuery(q, func() (ii []interface{}) {
for i := range args {
ii = append(ii, args[i])
@@ -459,7 +459,9 @@ func (c *conn) ID() string {
func (c *conn) BeginTx(ctx context.Context, txOptions driver.TxOptions) (_ driver.Tx, finalErr error) {
var tx currentTx
- onDone := trace.DatabaseSQLOnConnBegin(c.trace, &ctx, stack.FunctionID(""))
+ onDone := trace.DatabaseSQLOnConnBegin(c.trace, &ctx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).BeginTx"),
+ )
defer func() {
onDone(tx, finalErr)
}()
@@ -467,10 +469,10 @@ func (c *conn) BeginTx(ctx context.Context, txOptions driver.TxOptions) (_ drive
if c.currentTx != nil {
return nil, xerrors.WithStackTrace(
xerrors.Retryable(
- &ErrConnAlreadyHaveTx{
+ &ConnAlreadyHaveTxError{
currentTx: c.currentTx.ID(),
},
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
),
)
}
@@ -483,7 +485,7 @@ func (c *conn) BeginTx(ctx context.Context, txOptions driver.TxOptions) (_ drive
xerrors.WithStackTrace(
xerrors.Retryable(
fmt.Errorf("wrong query mode: %s", m.String()),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
xerrors.WithName("WRONG_QUERY_MODE"),
),
),
@@ -508,7 +510,7 @@ func (c *conn) Version(_ context.Context) (_ string, _ error) {
func (c *conn) IsTableExists(ctx context.Context, tableName string) (tableExists bool, finalErr error) {
tableName = c.normalizePath(tableName)
onDone := trace.DatabaseSQLOnConnIsTableExists(c.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*conn).IsTableExists"),
tableName,
)
defer func() {
diff --git a/internal/xsql/connector.go b/internal/xsql/connector.go
index a00246969..8009f66d1 100644
--- a/internal/xsql/connector.go
+++ b/internal/xsql/connector.go
@@ -262,10 +262,14 @@ func (c *Connector) idleCloser() (idleStopper func()) {
ctx, idleStopper = xcontext.WithCancel(context.Background())
go func() {
for {
+ idleThresholdTimer := c.clock.NewTimer(c.idleThreshold)
select {
case <-ctx.Done():
+ idleThresholdTimer.Stop()
+
return
- case <-c.clock.After(c.idleThreshold):
+ case <-idleThresholdTimer.Chan():
+ idleThresholdTimer.Stop() // no really need, stop for common style only
c.connsMtx.RLock()
conns := make([]*conn, 0, len(c.conns))
for cc := range c.conns {
@@ -313,7 +317,7 @@ func (c *Connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
var (
onDone = trace.DatabaseSQLOnConnectorConnect(
c.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*Connector).Connect"),
)
session table.ClosableSession
)
diff --git a/internal/xsql/dsn_test.go b/internal/xsql/dsn_test.go
index 557072701..d28c96ac3 100644
--- a/internal/xsql/dsn_test.go
+++ b/internal/xsql/dsn_test.go
@@ -13,8 +13,10 @@ func TestParse(t *testing.T) {
newConnector := func(opts ...ConnectorOption) *Connector {
c := &Connector{}
for _, opt := range opts {
- if err := opt.Apply(c); err != nil {
- t.Error(err)
+ if opt != nil {
+ if err := opt.Apply(c); err != nil {
+ t.Error(err)
+ }
}
}
diff --git a/internal/xsql/errors.go b/internal/xsql/errors.go
index 9d1af54c0..8dbcc7ce9 100644
--- a/internal/xsql/errors.go
+++ b/internal/xsql/errors.go
@@ -10,21 +10,21 @@ import (
var (
ErrUnsupported = driver.ErrSkip
errDeprecated = driver.ErrSkip
- errConnClosedEarly = xerrors.Retryable(errors.New("conn closed early"), xerrors.WithDeleteSession())
- errNotReadyConn = xerrors.Retryable(errors.New("conn not ready"), xerrors.WithDeleteSession())
+ errConnClosedEarly = xerrors.Retryable(errors.New("conn closed early"), xerrors.InvalidObject())
+ errNotReadyConn = xerrors.Retryable(errors.New("conn not ready"), xerrors.InvalidObject())
)
-type ErrConnAlreadyHaveTx struct {
+type ConnAlreadyHaveTxError struct {
currentTx string
}
-func (err *ErrConnAlreadyHaveTx) Error() string {
+func (err *ConnAlreadyHaveTxError) Error() string {
return "conn already have an open currentTx: " + err.currentTx
}
-func (err *ErrConnAlreadyHaveTx) As(target interface{}) bool {
+func (err *ConnAlreadyHaveTxError) As(target interface{}) bool {
switch t := target.(type) {
- case *ErrConnAlreadyHaveTx:
+ case *ConnAlreadyHaveTxError:
t.currentTx = err.currentTx
return true
diff --git a/internal/xsql/rows.go b/internal/xsql/rows.go
index 01dccaeb3..a9bb1572e 100644
--- a/internal/xsql/rows.go
+++ b/internal/xsql/rows.go
@@ -8,12 +8,12 @@ import (
"strings"
"sync"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/scanner"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql/badconn"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
var (
@@ -23,7 +23,7 @@ var (
_ driver.RowsColumnTypeNullable = &rows{}
_ driver.Rows = &single{}
- _ types.Scanner = &valuer{}
+ _ scanner.Scanner = &valuer{}
ignoreColumnPrefixName = "__discard_column_"
)
diff --git a/internal/xsql/stmt.go b/internal/xsql/stmt.go
index 82d80a916..75b571acd 100644
--- a/internal/xsql/stmt.go
+++ b/internal/xsql/stmt.go
@@ -31,7 +31,7 @@ var (
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (_ driver.Rows, finalErr error) {
onDone := trace.DatabaseSQLOnStmtQuery(s.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*stmt).QueryContext"),
s.stmtCtx, s.query,
)
defer func() {
@@ -50,7 +50,7 @@ func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (_ dr
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (_ driver.Result, finalErr error) {
onDone := trace.DatabaseSQLOnStmtExec(s.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*stmt).ExecContext"),
s.stmtCtx, s.query,
)
defer func() {
@@ -72,7 +72,9 @@ func (s *stmt) NumInput() int {
}
func (s *stmt) Close() (finalErr error) {
- onDone := trace.DatabaseSQLOnStmtClose(s.trace, &s.stmtCtx, stack.FunctionID(""))
+ onDone := trace.DatabaseSQLOnStmtClose(s.trace, &s.stmtCtx,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*stmt).Close"),
+ )
defer func() {
onDone(finalErr)
}()
diff --git a/internal/xsql/tx.go b/internal/xsql/tx.go
index 24eab6e73..d67813437 100644
--- a/internal/xsql/tx.go
+++ b/internal/xsql/tx.go
@@ -74,7 +74,7 @@ func (tx *tx) checkTxState() error {
func (tx *tx) Commit() (finalErr error) {
onDone := trace.DatabaseSQLOnTxCommit(tx.conn.trace, &tx.txCtx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*tx).Commit"),
tx,
)
defer func() {
@@ -96,7 +96,7 @@ func (tx *tx) Commit() (finalErr error) {
func (tx *tx) Rollback() (finalErr error) {
onDone := trace.DatabaseSQLOnTxRollback(tx.conn.trace, &tx.txCtx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*tx).Rollback"),
tx,
)
defer func() {
@@ -120,8 +120,8 @@ func (tx *tx) QueryContext(ctx context.Context, query string, args []driver.Name
_ driver.Rows, finalErr error,
) {
onDone := trace.DatabaseSQLOnTxQuery(tx.conn.trace, &ctx,
- stack.FunctionID(""),
- tx.txCtx, tx, query, true,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*tx).QueryContext"),
+ tx.txCtx, tx, query,
)
defer func() {
onDone(finalErr)
@@ -132,18 +132,18 @@ func (tx *tx) QueryContext(ctx context.Context, query string, args []driver.Name
xerrors.WithStackTrace(
xerrors.Retryable(
fmt.Errorf("wrong query mode: %s", m.String()),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
xerrors.WithName("WRONG_QUERY_MODE"),
),
),
)
}
- query, params, err := tx.conn.normalize(query, args...)
+ query, parameters, err := tx.conn.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
res, err := tx.tx.Execute(ctx,
- query, params, tx.conn.dataQueryOptions(ctx)...,
+ query, ¶meters, tx.conn.dataQueryOptions(ctx)...,
)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
@@ -162,8 +162,8 @@ func (tx *tx) ExecContext(ctx context.Context, query string, args []driver.Named
_ driver.Result, finalErr error,
) {
onDone := trace.DatabaseSQLOnTxExec(tx.conn.trace, &ctx,
- stack.FunctionID(""),
- tx.txCtx, tx, query, true,
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*tx).ExecContext"),
+ tx.txCtx, tx, query,
)
defer func() {
onDone(finalErr)
@@ -174,18 +174,18 @@ func (tx *tx) ExecContext(ctx context.Context, query string, args []driver.Named
xerrors.WithStackTrace(
xerrors.Retryable(
fmt.Errorf("wrong query mode: %s", m.String()),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
xerrors.WithName("WRONG_QUERY_MODE"),
),
),
)
}
- query, params, err := tx.conn.normalize(query, args...)
+ query, parameters, err := tx.conn.normalize(query, args...)
if err != nil {
return nil, xerrors.WithStackTrace(err)
}
_, err = tx.tx.Execute(ctx,
- query, params, tx.conn.dataQueryOptions(ctx)...,
+ query, ¶meters, tx.conn.dataQueryOptions(ctx)...,
)
if err != nil {
return nil, badconn.Map(xerrors.WithStackTrace(err))
@@ -196,7 +196,7 @@ func (tx *tx) ExecContext(ctx context.Context, query string, args []driver.Named
func (tx *tx) PrepareContext(ctx context.Context, query string) (_ driver.Stmt, finalErr error) {
onDone := trace.DatabaseSQLOnTxPrepare(tx.conn.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*tx).PrepareContext"),
&tx.txCtx, tx, query,
)
defer func() {
diff --git a/internal/xsql/tx_fake.go b/internal/xsql/tx_fake.go
index 662fcd078..459aba718 100644
--- a/internal/xsql/tx_fake.go
+++ b/internal/xsql/tx_fake.go
@@ -5,7 +5,6 @@ import (
"database/sql/driver"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql/badconn"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
@@ -20,7 +19,7 @@ type txFake struct {
func (tx *txFake) PrepareContext(ctx context.Context, query string) (_ driver.Stmt, finalErr error) {
onDone := trace.DatabaseSQLOnTxPrepare(tx.conn.trace, &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*txFake).PrepareContext"),
&tx.beginCtx, tx, query,
)
defer func() {
@@ -59,7 +58,7 @@ func (tx *txFake) ID() string {
func (tx *txFake) Commit() (err error) {
onDone := trace.DatabaseSQLOnTxCommit(tx.conn.trace, &tx.ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*txFake).Commit"),
tx,
)
defer func() {
@@ -77,7 +76,7 @@ func (tx *txFake) Commit() (err error) {
func (tx *txFake) Rollback() (err error) {
onDone := trace.DatabaseSQLOnTxRollback(tx.conn.trace, &tx.ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*txFake).Rollback"),
tx,
)
defer func() {
@@ -98,8 +97,8 @@ func (tx *txFake) QueryContext(ctx context.Context, query string, args []driver.
) {
onDone := trace.DatabaseSQLOnTxQuery(
tx.conn.trace, &ctx,
- stack.FunctionID(""),
- tx.ctx, tx, query, xcontext.IsIdempotent(ctx),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*txFake).QueryContext"),
+ tx.ctx, tx, query,
)
defer func() {
onDone(err)
@@ -117,8 +116,8 @@ func (tx *txFake) ExecContext(ctx context.Context, query string, args []driver.N
) {
onDone := trace.DatabaseSQLOnTxExec(
tx.conn.trace, &ctx,
- stack.FunctionID(""),
- tx.ctx, tx, query, xcontext.IsIdempotent(ctx),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/internal/xsql.(*txFake).ExecContext"),
+ tx.ctx, tx, query,
)
defer func() {
onDone(err)
diff --git a/internal/xsql/valuer.go b/internal/xsql/valuer.go
index e99b54b0f..6171908b1 100644
--- a/internal/xsql/valuer.go
+++ b/internal/xsql/valuer.go
@@ -1,12 +1,14 @@
package xsql
-import "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
+import (
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/scanner"
+)
type valuer struct {
v interface{}
}
-func (v *valuer) UnmarshalYDB(raw types.RawValue) error {
+func (v *valuer) UnmarshalYDB(raw scanner.RawValue) error {
v.v = raw.Any()
return nil
diff --git a/internal/xsync/once.go b/internal/xsync/once.go
new file mode 100644
index 000000000..35f5ed0aa
--- /dev/null
+++ b/internal/xsync/once.go
@@ -0,0 +1,61 @@
+package xsync
+
+import (
+ "context"
+ "sync"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/closer"
+)
+
+func OnceFunc(f func(ctx context.Context) error) func(ctx context.Context) error {
+ var once sync.Once
+
+ return func(ctx context.Context) (err error) {
+ once.Do(func() {
+ err = f(ctx)
+ })
+
+ return err
+ }
+}
+
+type Once[T closer.Closer] struct {
+ f func() T
+ once sync.Once
+ mutex sync.RWMutex
+ t T
+}
+
+func OnceValue[T closer.Closer](f func() T) *Once[T] {
+ return &Once[T]{f: f}
+}
+
+func (v *Once[T]) Close(ctx context.Context) (err error) {
+ has := true
+ v.once.Do(func() {
+ has = false
+ })
+
+ if has {
+ v.mutex.RLock()
+ defer v.mutex.RUnlock()
+
+ return v.t.Close(ctx)
+ }
+
+ return nil
+}
+
+func (v *Once[T]) Get() T {
+ v.once.Do(func() {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ v.t = v.f()
+ })
+
+ v.mutex.RLock()
+ defer v.mutex.RUnlock()
+
+ return v.t
+}
diff --git a/internal/xsync/once_test.go b/internal/xsync/once_test.go
new file mode 100644
index 000000000..003e9a7e7
--- /dev/null
+++ b/internal/xsync/once_test.go
@@ -0,0 +1,93 @@
+package xsync
+
+import (
+ "context"
+ "errors"
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+)
+
+func TestOnceFunc(t *testing.T) {
+ var (
+ ctx = xtest.Context(t)
+ cnt = 0
+ )
+ f := OnceFunc(func(ctx context.Context) error {
+ cnt++
+
+ return nil
+ })
+ require.Equal(t, 0, cnt)
+ require.NoError(t, f(ctx))
+ require.Equal(t, 1, cnt)
+ require.NoError(t, f(ctx))
+ require.Equal(t, 1, cnt)
+}
+
+type testCloser struct {
+ value int
+ inited bool
+ closed bool
+ closeErr error
+}
+
+func (c *testCloser) Close(ctx context.Context) error {
+ c.closed = true
+
+ return c.closeErr
+}
+
+func TestOnceValue(t *testing.T) {
+ ctx := xtest.Context(t)
+ t.Run("Race", func(t *testing.T) {
+ counter := 0
+ once := OnceValue(func() *testCloser {
+ counter++
+
+ return &testCloser{value: counter}
+ })
+ var wg sync.WaitGroup
+ wg.Add(1000)
+ for range make([]struct{}, 1000) {
+ go func() {
+ defer wg.Done()
+ v := once.Get()
+ require.Equal(t, 1, v.value)
+ }()
+ }
+ wg.Wait()
+ })
+ t.Run("GetBeforeClose", func(t *testing.T) {
+ constCloseErr := errors.New("")
+ once := OnceValue(func() *testCloser {
+ return &testCloser{
+ inited: true,
+ closeErr: constCloseErr,
+ }
+ })
+ v := once.Get()
+ require.True(t, v.inited)
+ require.False(t, v.closed)
+ err := once.Close(ctx)
+ require.ErrorIs(t, err, constCloseErr)
+ require.True(t, v.inited)
+ require.True(t, v.closed)
+ })
+ t.Run("CloseBeforeGet", func(t *testing.T) {
+ constCloseErr := errors.New("")
+ once := OnceValue(func() *testCloser {
+ return &testCloser{
+ inited: true,
+ closeErr: constCloseErr,
+ }
+ })
+ err := once.Close(ctx)
+ require.NoError(t, err)
+ v := once.Get()
+ require.Nil(t, v)
+ })
+}
diff --git a/internal/xtest/call_method.go b/internal/xtest/call_method.go
new file mode 100644
index 000000000..a7e060a8a
--- /dev/null
+++ b/internal/xtest/call_method.go
@@ -0,0 +1,25 @@
+package xtest
+
+import (
+ "reflect"
+)
+
+func CallMethod(object any, name string, args ...any) []any {
+ method := reflect.ValueOf(object).MethodByName(name)
+
+ inputs := make([]reflect.Value, len(args))
+
+ for i := range args {
+ inputs[i] = reflect.ValueOf(args[i])
+ }
+
+ output := method.Call(inputs)
+
+ result := make([]any, len(output))
+
+ for i := range output {
+ result[i] = output[i].Interface()
+ }
+
+ return result
+}
diff --git a/internal/xtest/call_method_test.go b/internal/xtest/call_method_test.go
new file mode 100644
index 000000000..fe721761d
--- /dev/null
+++ b/internal/xtest/call_method_test.go
@@ -0,0 +1,39 @@
+package xtest
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestCallMethod(t *testing.T) {
+ object := bytes.NewBuffer(nil)
+
+ result := CallMethod(object, "WriteString", "Hello world!")
+ n := result[0].(int)
+ err := result[1]
+
+ require.Equal(t, 12, n)
+ require.Nil(t, err)
+
+ result = CallMethod(object, "String")
+
+ str, ok := result[0].(string)
+ require.True(t, ok)
+
+ require.Equal(t, object.String(), str)
+
+ require.Panics(t, func() {
+ CallMethod(object, "NonameMethod")
+ })
+
+ require.Panics(t, func() {
+ CallMethod(object, "String", "wrong", "arguments", "count")
+ })
+
+ require.Panics(t, func() {
+ // Wrong argument type.
+ CallMethod(object, "WriteString", 123)
+ })
+}
diff --git a/internal/xtest/grpclogger.go b/internal/xtest/grpclogger.go
index b36c05483..3506d4ccb 100644
--- a/internal/xtest/grpclogger.go
+++ b/internal/xtest/grpclogger.go
@@ -16,7 +16,7 @@ var globalLastStreamID = int64(0)
//
// db, err := ydb.Open(context.Background(), connectionString,
// ...
-// ydb.With(config.WithGrpcOptions(grpc.WithChainUnaryInterceptor(xtest.NewGrpcLogger(t).UnaryClientInterceptor))),
+// ydb.Change(config.WithGrpcOptions(grpc.WithChainUnaryInterceptor(xtest.NewGrpcLogger(t).UnaryClientInterceptor))),
// )
type GrpcLogger struct {
t testing.TB
diff --git a/internal/xtest/manytimes.go b/internal/xtest/manytimes.go
index cd84603fb..4dc7f1ccb 100644
--- a/internal/xtest/manytimes.go
+++ b/internal/xtest/manytimes.go
@@ -31,9 +31,9 @@ func TestManyTimes(t testing.TB, test TestFunc, opts ...TestManyTimesOption) {
stopAfter: time.Second,
}
- for _, o := range opts {
- if o != nil {
- o(&options)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&options)
}
}
diff --git a/internal/xtest/to_json.go b/internal/xtest/to_json.go
new file mode 100644
index 000000000..add66425c
--- /dev/null
+++ b/internal/xtest/to_json.go
@@ -0,0 +1,9 @@
+package xtest
+
+import "encoding/json"
+
+func ToJSON(v interface{}) string {
+ b, _ := json.MarshalIndent(v, "", "\t") //nolint:errchkjson
+
+ return string(b)
+}
diff --git a/internal/xtest/to_json_test.go b/internal/xtest/to_json_test.go
new file mode 100644
index 000000000..861640068
--- /dev/null
+++ b/internal/xtest/to_json_test.go
@@ -0,0 +1,58 @@
+package xtest
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestToJSON(t *testing.T) {
+ for _, tt := range []struct {
+ name string
+ v interface{}
+ s string
+ }{
+ {
+ name: CurrentFileLine(),
+ v: int64(123),
+ s: "123",
+ },
+ {
+ name: CurrentFileLine(),
+ v: struct {
+ A string
+ B int64
+ C bool
+ }{
+ A: "123",
+ B: 123,
+ C: true,
+ },
+ s: "{\n\t\"A\": \"123\",\n\t\"B\": 123,\n\t\"C\": true\n}",
+ },
+ {
+ name: CurrentFileLine(),
+ v: map[string]struct {
+ A string
+ B int64
+ C bool
+ }{
+ "abc": {
+ A: "123",
+ B: 123,
+ C: true,
+ },
+ "def": {
+ A: "456",
+ B: 456,
+ C: false,
+ },
+ },
+ s: "{\n\t\"abc\": {\n\t\t\"A\": \"123\",\n\t\t\"B\": 123,\n\t\t\"C\": true\n\t},\n\t\"def\": {\n\t\t\"A\": \"456\",\n\t\t\"B\": 456,\n\t\t\"C\": false\n\t}\n}", //nolint:lll
+ },
+ } {
+ t.Run(tt.name, func(t *testing.T) {
+ require.Equal(t, tt.s, ToJSON(tt.v))
+ })
+ }
+}
diff --git a/log/coordination.go b/log/coordination.go
index 1485118de..865b8cd9c 100644
--- a/log/coordination.go
+++ b/log/coordination.go
@@ -1,10 +1,348 @@
package log
import (
+ "context"
+ "strconv"
+ "time"
+
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
// Coordination makes trace.Coordination with logging events from details
func Coordination(l Logger, d trace.Detailer, opts ...Option) (t trace.Coordination) {
- return t
+ return internalCoordination(wrapLogger(l, opts...), d)
+}
+
+func internalCoordination(
+ l *wrapper, //nolint:interfacer
+ d trace.Detailer,
+) trace.Coordination {
+ return trace.Coordination{
+ OnNew: func(info trace.CoordinationNewStartInfo) func(trace.CoordinationNewDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "new")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.CoordinationNewDoneInfo) {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ },
+ OnCreateNode: func(info trace.CoordinationCreateNodeStartInfo) func(trace.CoordinationCreateNodeDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "node", "create")
+ l.Log(ctx, "start",
+ String("path", info.Path),
+ )
+ start := time.Now()
+
+ return func(info trace.CoordinationCreateNodeDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "fail",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnAlterNode: func(info trace.CoordinationAlterNodeStartInfo) func(trace.CoordinationAlterNodeDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "node", "alter")
+ l.Log(ctx, "start",
+ String("path", info.Path),
+ )
+ start := time.Now()
+
+ return func(info trace.CoordinationAlterNodeDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "fail",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnDropNode: func(info trace.CoordinationDropNodeStartInfo) func(trace.CoordinationDropNodeDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "node", "drop")
+ l.Log(ctx, "start",
+ String("path", info.Path),
+ )
+ start := time.Now()
+
+ return func(info trace.CoordinationDropNodeDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "fail",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnDescribeNode: func(info trace.CoordinationDescribeNodeStartInfo) func(trace.CoordinationDescribeNodeDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "node", "describe")
+ l.Log(ctx, "start",
+ String("path", info.Path),
+ )
+ start := time.Now()
+
+ return func(info trace.CoordinationDescribeNodeDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "fail",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnSession: func(info trace.CoordinationSessionStartInfo) func(trace.CoordinationSessionDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "node", "describe")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.CoordinationSessionDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "fail",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnClose: func(info trace.CoordinationCloseStartInfo) func(trace.CoordinationCloseDoneInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "coordination", "close")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.CoordinationCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "fail",
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnStreamNew: func(
+ info trace.CoordinationStreamNewStartInfo,
+ ) func(
+ info trace.CoordinationStreamNewDoneInfo,
+ ) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "stream", "new")
+ l.Log(ctx, "stream")
+ start := time.Now()
+
+ return func(info trace.CoordinationStreamNewDoneInfo) {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ },
+ OnSessionStarted: func(info trace.CoordinationSessionStartedInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "started")
+ l.Log(ctx, "",
+ String("sessionID", strconv.FormatUint(info.SessionID, 10)),
+ String("expectedSessionID", strconv.FormatUint(info.SessionID, 10)),
+ )
+ },
+ OnSessionStartTimeout: func(info trace.CoordinationSessionStartTimeoutInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "start", "timeout")
+ l.Log(ctx, "",
+ Stringer("timeout", info.Timeout),
+ )
+ },
+ OnSessionKeepAliveTimeout: func(info trace.CoordinationSessionKeepAliveTimeoutInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "keepAlive", "timeout")
+ l.Log(ctx, "",
+ Stringer("timeout", info.Timeout),
+ Stringer("lastGoodResponseTime", info.LastGoodResponseTime),
+ )
+ },
+ OnSessionStopped: func(info trace.CoordinationSessionStoppedInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "stopped")
+ l.Log(ctx, "",
+ String("sessionID", strconv.FormatUint(info.SessionID, 10)),
+ String("expectedSessionID", strconv.FormatUint(info.SessionID, 10)),
+ )
+ },
+ OnSessionStopTimeout: func(info trace.CoordinationSessionStopTimeoutInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "stop", "timeout")
+ l.Log(ctx, "",
+ Stringer("timeout", info.Timeout),
+ )
+ },
+ OnSessionClientTimeout: func(info trace.CoordinationSessionClientTimeoutInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "client", "timeout")
+ l.Log(ctx, "",
+ Stringer("timeout", info.Timeout),
+ Stringer("lastGoodResponseTime", info.LastGoodResponseTime),
+ )
+ },
+ OnSessionServerExpire: func(info trace.CoordinationSessionServerExpireInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "server", "expire")
+ l.Log(ctx, "",
+ Stringer("failure", info.Failure),
+ )
+ },
+ OnSessionServerError: func(info trace.CoordinationSessionServerErrorInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "server", "error")
+ l.Log(ctx, "",
+ Stringer("failure", info.Failure),
+ )
+ },
+ OnSessionReceive: func(
+ info trace.CoordinationSessionReceiveStartInfo,
+ ) func(
+ info trace.CoordinationSessionReceiveDoneInfo,
+ ) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "receive")
+ l.Log(ctx, "receive")
+ start := time.Now()
+
+ return func(info trace.CoordinationSessionReceiveDoneInfo) {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Error(info.Error),
+ Stringer("response", info.Response),
+ versionField(),
+ )
+ }
+ },
+ OnSessionReceiveUnexpected: func(info trace.CoordinationSessionReceiveUnexpectedInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "receive", "unexpected")
+ l.Log(ctx, "",
+ Stringer("response", info.Response),
+ )
+ },
+ OnSessionStop: func(info trace.CoordinationSessionStopInfo) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "stop")
+ l.Log(ctx, "",
+ String("sessionID", strconv.FormatUint(info.SessionID, 10)),
+ )
+ },
+ OnSessionStart: func(
+ info trace.CoordinationSessionStartStartInfo,
+ ) func(
+ info trace.CoordinationSessionStartDoneInfo,
+ ) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "start")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.CoordinationSessionStartDoneInfo) {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ },
+ OnSessionSend: func(
+ info trace.CoordinationSessionSendStartInfo,
+ ) func(
+ info trace.CoordinationSessionSendDoneInfo,
+ ) {
+ if d.Details()&trace.CoordinationEvents == 0 {
+ return nil
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "coordination", "session", "send")
+ l.Log(ctx, "start",
+ Stringer("request", info.Request),
+ )
+ start := time.Now()
+
+ return func(info trace.CoordinationSessionSendDoneInfo) {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ },
+ }
}
diff --git a/log/driver.go b/log/driver.go
index a94ffb262..fb9c8b1a1 100644
--- a/log/driver.go
+++ b/log/driver.go
@@ -13,267 +13,185 @@ func Driver(l Logger, d trace.Detailer, opts ...Option) (t trace.Driver) {
return internalDriver(wrapLogger(l, opts...), d)
}
-func internalDriver(l Logger, d trace.Detailer) (t trace.Driver) { //nolint:gocyclo
- t.OnResolve = func(
- info trace.DriverResolveStartInfo,
- ) func(
- trace.DriverResolveDoneInfo,
- ) {
- if d.Details()&trace.DriverResolverEvents == 0 {
- return nil
- }
- ctx := with(context.Background(), TRACE, "ydb", "driver", "resolver", "update")
- target := info.Target
- addresses := info.Resolved
- l.Log(ctx, "start",
- String("target", target),
- Strings("resolved", addresses),
- )
-
- return func(info trace.DriverResolveDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- String("target", target),
- Strings("resolved", addresses),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- String("target", target),
- Strings("resolved", addresses),
- versionField(),
- )
+func internalDriver(l Logger, d trace.Detailer) trace.Driver { //nolint:gocyclo
+ return trace.Driver{
+ OnResolve: func(
+ info trace.DriverResolveStartInfo,
+ ) func(
+ trace.DriverResolveDoneInfo,
+ ) {
+ if d.Details()&trace.DriverResolverEvents == 0 {
+ return nil
}
- }
- }
- t.OnInit = func(info trace.DriverInitStartInfo) func(trace.DriverInitDoneInfo) {
- if d.Details()&trace.DriverEvents == 0 {
- return nil
- }
- endpoint := info.Endpoint
- database := info.Database
- secure := info.Secure
- ctx := with(*info.Context, DEBUG, "ydb", "driver", "resolver", "init")
- l.Log(ctx, "start",
- String("endpoint", endpoint),
- String("database", database),
- Bool("secure", secure),
- )
- start := time.Now()
+ ctx := with(context.Background(), TRACE, "ydb", "driver", "resolver", "update")
+ target := info.Target
+ addresses := info.Resolved
+ l.Log(ctx, "start",
+ String("target", target),
+ Strings("resolved", addresses),
+ )
- return func(info trace.DriverInitDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- String("endpoint", endpoint),
- String("database", database),
- Bool("secure", secure),
- latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "failed",
- Error(info.Error),
- String("endpoint", endpoint),
- String("database", database),
- Bool("secure", secure),
- latencyField(start),
- versionField(),
- )
+ return func(info trace.DriverResolveDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ String("target", target),
+ Strings("resolved", addresses),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ String("target", target),
+ Strings("resolved", addresses),
+ versionField(),
+ )
+ }
}
- }
- }
- t.OnClose = func(info trace.DriverCloseStartInfo) func(trace.DriverCloseDoneInfo) {
- if d.Details()&trace.DriverEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "resolver", "close")
- l.Log(ctx, "start")
- start := time.Now()
+ },
+ OnInit: func(info trace.DriverInitStartInfo) func(trace.DriverInitDoneInfo) {
+ if d.Details()&trace.DriverEvents == 0 {
+ return nil
+ }
+ endpoint := info.Endpoint
+ database := info.Database
+ secure := info.Secure
+ ctx := with(*info.Context, DEBUG, "ydb", "driver", "resolver", "init")
+ l.Log(ctx, "start",
+ String("endpoint", endpoint),
+ String("database", database),
+ Bool("secure", secure),
+ )
+ start := time.Now()
- return func(info trace.DriverCloseDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- latencyField(start),
- versionField(),
- )
+ return func(info trace.DriverInitDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ String("endpoint", endpoint),
+ String("database", database),
+ Bool("secure", secure),
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "failed",
+ Error(info.Error),
+ String("endpoint", endpoint),
+ String("database", database),
+ Bool("secure", secure),
+ latencyField(start),
+ versionField(),
+ )
+ }
}
- }
- }
- t.OnConnDial = func(info trace.DriverConnDialStartInfo) func(trace.DriverConnDialDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "dial")
- endpoint := info.Endpoint
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- )
- start := time.Now()
+ },
+ OnClose: func(info trace.DriverCloseStartInfo) func(trace.DriverCloseDoneInfo) {
+ if d.Details()&trace.DriverEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "resolver", "close")
+ l.Log(ctx, "start")
+ start := time.Now()
- return func(info trace.DriverConnDialDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- Stringer("endpoint", endpoint),
- latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- Stringer("endpoint", endpoint),
- latencyField(start),
- versionField(),
- )
+ return func(info trace.DriverCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ latencyField(start),
+ versionField(),
+ )
+ }
}
- }
- }
- t.OnConnStateChange = func(info trace.DriverConnStateChangeStartInfo) func(trace.DriverConnStateChangeDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(context.Background(), TRACE, "ydb", "driver", "conn", "state", "change")
- endpoint := info.Endpoint
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- Stringer("state", info.State),
- )
- start := time.Now()
+ },
+ OnConnDial: func(info trace.DriverConnDialStartInfo) func(trace.DriverConnDialDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "dial")
+ endpoint := info.Endpoint
+ l.Log(ctx, "start",
+ Stringer("endpoint", endpoint),
+ )
+ start := time.Now()
- return func(info trace.DriverConnStateChangeDoneInfo) {
- l.Log(ctx, "done",
+ return func(info trace.DriverConnDialDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ Stringer("endpoint", endpoint),
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ Stringer("endpoint", endpoint),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnConnStateChange: func(info trace.DriverConnStateChangeStartInfo) func(trace.DriverConnStateChangeDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(context.Background(), TRACE, "ydb", "driver", "conn", "state", "change")
+ endpoint := info.Endpoint
+ l.Log(ctx, "start",
Stringer("endpoint", endpoint),
- latencyField(start),
Stringer("state", info.State),
)
- }
- }
- t.OnConnPark = func(info trace.DriverConnParkStartInfo) func(trace.DriverConnParkDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "park")
- endpoint := info.Endpoint
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- )
- start := time.Now()
+ start := time.Now()
- return func(info trace.DriverConnParkDoneInfo) {
- if info.Error == nil {
+ return func(info trace.DriverConnStateChangeDoneInfo) {
l.Log(ctx, "done",
Stringer("endpoint", endpoint),
latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- Stringer("endpoint", endpoint),
- latencyField(start),
- versionField(),
+ Stringer("state", info.State),
)
}
- }
- }
- t.OnConnClose = func(info trace.DriverConnCloseStartInfo) func(trace.DriverConnCloseDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "close")
- endpoint := info.Endpoint
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- )
- start := time.Now()
-
- return func(info trace.DriverConnCloseDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- Stringer("endpoint", endpoint),
- latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- Stringer("endpoint", endpoint),
- latencyField(start),
- versionField(),
- )
+ },
+ OnConnClose: func(info trace.DriverConnCloseStartInfo) func(trace.DriverConnCloseDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
}
- }
- }
- t.OnConnInvoke = func(info trace.DriverConnInvokeStartInfo) func(trace.DriverConnInvokeDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "invoke")
- endpoint := info.Endpoint
- method := string(info.Method)
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- String("method", method),
- )
- start := time.Now()
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "close")
+ endpoint := info.Endpoint
+ l.Log(ctx, "start",
+ Stringer("endpoint", endpoint),
+ )
+ start := time.Now()
- return func(info trace.DriverConnInvokeDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- Stringer("endpoint", endpoint),
- String("method", method),
- latencyField(start),
- Stringer("metadata", metadata(info.Metadata)),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- Stringer("endpoint", endpoint),
- String("method", method),
- latencyField(start),
- Stringer("metadata", metadata(info.Metadata)),
- versionField(),
- )
+ return func(info trace.DriverConnCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ Stringer("endpoint", endpoint),
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ Stringer("endpoint", endpoint),
+ latencyField(start),
+ versionField(),
+ )
+ }
}
- }
- }
- t.OnConnNewStream = func(
- info trace.DriverConnNewStreamStartInfo,
- ) func(
- trace.DriverConnNewStreamRecvInfo,
- ) func(
- trace.DriverConnNewStreamDoneInfo,
- ) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "new", "stream")
- endpoint := info.Endpoint
- method := string(info.Method)
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- String("method", method),
- )
- start := time.Now()
-
- return func(info trace.DriverConnNewStreamRecvInfo) func(trace.DriverConnNewStreamDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "intermediate receive",
- Stringer("endpoint", endpoint),
- String("method", method),
- latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "intermediate fail",
- Error(info.Error),
- Stringer("endpoint", endpoint),
- String("method", method),
- latencyField(start),
- versionField(),
- )
+ },
+ OnConnInvoke: func(info trace.DriverConnInvokeStartInfo) func(trace.DriverConnInvokeDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
}
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "invoke")
+ endpoint := info.Endpoint
+ method := string(info.Method)
+ l.Log(ctx, "start",
+ Stringer("endpoint", endpoint),
+ String("method", method),
+ )
+ start := time.Now()
- return func(info trace.DriverConnNewStreamDoneInfo) {
+ return func(info trace.DriverConnInvokeDoneInfo) {
if info.Error == nil {
l.Log(ctx, "done",
Stringer("endpoint", endpoint),
@@ -292,191 +210,293 @@ func internalDriver(l Logger, d trace.Detailer) (t trace.Driver) { //nolint:gocy
)
}
}
- }
- }
- t.OnConnBan = func(info trace.DriverConnBanStartInfo) func(trace.DriverConnBanDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "ban")
- endpoint := info.Endpoint
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- NamedError("cause", info.Cause),
- versionField(),
- )
- start := time.Now()
-
- return func(info trace.DriverConnBanDoneInfo) {
- l.Log(WithLevel(ctx, WARN), "done",
+ },
+ OnConnNewStream: func(
+ info trace.DriverConnNewStreamStartInfo,
+ ) func(
+ trace.DriverConnNewStreamDoneInfo,
+ ) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "stream", "New")
+ endpoint := info.Endpoint
+ method := string(info.Method)
+ l.Log(ctx, "start",
Stringer("endpoint", endpoint),
- latencyField(start),
- Stringer("state", info.State),
- versionField(),
+ String("method", method),
)
- }
- }
- t.OnConnAllow = func(info trace.DriverConnAllowStartInfo) func(trace.DriverConnAllowDoneInfo) {
- if d.Details()&trace.DriverConnEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "allow")
- endpoint := info.Endpoint
- l.Log(ctx, "start",
- Stringer("endpoint", endpoint),
- )
- start := time.Now()
+ start := time.Now()
+
+ return func(info trace.DriverConnNewStreamDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ Stringer("endpoint", endpoint),
+ String("method", method),
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ Stringer("endpoint", endpoint),
+ String("method", method),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnConnStreamCloseSend: func(info trace.DriverConnStreamCloseSendStartInfo) func(
+ trace.DriverConnStreamCloseSendDoneInfo,
+ ) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "stream", "CloseSend")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.DriverConnStreamCloseSendDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnConnStreamSendMsg: func(info trace.DriverConnStreamSendMsgStartInfo) func(trace.DriverConnStreamSendMsgDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "stream", "SendMsg")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.DriverConnStreamSendMsgDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnConnStreamRecvMsg: func(info trace.DriverConnStreamRecvMsgStartInfo) func(trace.DriverConnStreamRecvMsgDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "stream", "RecvMsg")
+ l.Log(ctx, "start")
+ start := time.Now()
- return func(info trace.DriverConnAllowDoneInfo) {
- l.Log(ctx, "done",
+ return func(info trace.DriverConnStreamRecvMsgDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnConnBan: func(info trace.DriverConnBanStartInfo) func(trace.DriverConnBanDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "ban")
+ endpoint := info.Endpoint
+ cause := info.Cause
+ l.Log(ctx, "start",
Stringer("endpoint", endpoint),
- latencyField(start),
- Stringer("state", info.State),
+ NamedError("cause", cause),
)
- }
- }
- t.OnRepeaterWakeUp = func(info trace.DriverRepeaterWakeUpStartInfo) func(trace.DriverRepeaterWakeUpDoneInfo) {
- if d.Details()&trace.DriverRepeaterEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "repeater", "wake", "up")
- name := info.Name
- event := info.Event
- l.Log(ctx, "start",
- String("name", name),
- String("event", event),
- )
- start := time.Now()
+ start := time.Now()
- return func(info trace.DriverRepeaterWakeUpDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- String("name", name),
- String("event", event),
- latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "failed",
- Error(info.Error),
- String("name", name),
- String("event", event),
+ return func(info trace.DriverConnBanDoneInfo) {
+ l.Log(WithLevel(ctx, WARN), "done",
+ Stringer("endpoint", endpoint),
latencyField(start),
+ Stringer("state", info.State),
+ NamedError("cause", cause),
versionField(),
)
}
- }
- }
- t.OnBalancerInit = func(info trace.DriverBalancerInitStartInfo) func(trace.DriverBalancerInitDoneInfo) {
- if d.Details()&trace.DriverBalancerEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "init")
- l.Log(ctx, "start")
- start := time.Now()
-
- return func(info trace.DriverBalancerInitDoneInfo) {
- l.Log(WithLevel(ctx, INFO), "done",
- latencyField(start),
+ },
+ OnConnAllow: func(info trace.DriverConnAllowStartInfo) func(trace.DriverConnAllowDoneInfo) {
+ if d.Details()&trace.DriverConnEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "conn", "allow")
+ endpoint := info.Endpoint
+ l.Log(ctx, "start",
+ Stringer("endpoint", endpoint),
)
- }
- }
- t.OnBalancerClose = func(info trace.DriverBalancerCloseStartInfo) func(trace.DriverBalancerCloseDoneInfo) {
- if d.Details()&trace.DriverBalancerEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "close")
- l.Log(ctx, "start")
- start := time.Now()
+ start := time.Now()
- return func(info trace.DriverBalancerCloseDoneInfo) {
- if info.Error == nil {
+ return func(info trace.DriverConnAllowDoneInfo) {
l.Log(ctx, "done",
+ Stringer("endpoint", endpoint),
latencyField(start),
- )
- } else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- latencyField(start),
- versionField(),
+ Stringer("state", info.State),
)
}
- }
- }
- t.OnBalancerChooseEndpoint = func(
- info trace.DriverBalancerChooseEndpointStartInfo,
- ) func(
- trace.DriverBalancerChooseEndpointDoneInfo,
- ) {
- if d.Details()&trace.DriverBalancerEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "choose", "endpoint")
- l.Log(ctx, "start")
- start := time.Now()
+ },
+ OnRepeaterWakeUp: func(info trace.DriverRepeaterWakeUpStartInfo) func(trace.DriverRepeaterWakeUpDoneInfo) {
+ if d.Details()&trace.DriverRepeaterEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "repeater", "wake", "up")
+ name := info.Name
+ event := info.Event
+ l.Log(ctx, "start",
+ String("name", name),
+ String("event", event),
+ )
+ start := time.Now()
- return func(info trace.DriverBalancerChooseEndpointDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- latencyField(start),
- Stringer("endpoint", info.Endpoint),
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "failed",
- Error(info.Error),
+ return func(info trace.DriverRepeaterWakeUpDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ String("name", name),
+ String("event", event),
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "failed",
+ Error(info.Error),
+ String("name", name),
+ String("event", event),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnBalancerInit: func(info trace.DriverBalancerInitStartInfo) func(trace.DriverBalancerInitDoneInfo) {
+ if d.Details()&trace.DriverBalancerEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "init")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.DriverBalancerInitDoneInfo) {
+ l.Log(WithLevel(ctx, INFO), "done",
latencyField(start),
- versionField(),
)
}
- }
- }
- t.OnBalancerUpdate = func(
- info trace.DriverBalancerUpdateStartInfo,
- ) func(
- trace.DriverBalancerUpdateDoneInfo,
- ) {
- if d.Details()&trace.DriverBalancerEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "update")
- l.Log(ctx, "start",
- Bool("needLocalDC", info.NeedLocalDC),
- )
- start := time.Now()
+ },
+ OnBalancerClose: func(info trace.DriverBalancerCloseStartInfo) func(trace.DriverBalancerCloseDoneInfo) {
+ if d.Details()&trace.DriverBalancerEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "close")
+ l.Log(ctx, "start")
+ start := time.Now()
- return func(info trace.DriverBalancerUpdateDoneInfo) {
- l.Log(ctx, "done",
- latencyField(start),
- Stringer("endpoints", endpoints(info.Endpoints)),
- Stringer("added", endpoints(info.Added)),
- Stringer("dropped", endpoints(info.Dropped)),
- String("detectedLocalDC", info.LocalDC),
+ return func(info trace.DriverBalancerCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ l.Log(WithLevel(ctx, WARN), "failed",
+ Error(info.Error),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnBalancerChooseEndpoint: func(
+ info trace.DriverBalancerChooseEndpointStartInfo,
+ ) func(
+ trace.DriverBalancerChooseEndpointDoneInfo,
+ ) {
+ if d.Details()&trace.DriverBalancerEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "choose", "endpoint")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.DriverBalancerChooseEndpointDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Stringer("endpoint", info.Endpoint),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "failed",
+ Error(info.Error),
+ latencyField(start),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnBalancerUpdate: func(
+ info trace.DriverBalancerUpdateStartInfo,
+ ) func(
+ trace.DriverBalancerUpdateDoneInfo,
+ ) {
+ if d.Details()&trace.DriverBalancerEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "balancer", "update")
+ l.Log(ctx, "start",
+ Bool("needLocalDC", info.NeedLocalDC),
)
- }
- }
- t.OnGetCredentials = func(info trace.DriverGetCredentialsStartInfo) func(trace.DriverGetCredentialsDoneInfo) {
- if d.Details()&trace.DriverCredentialsEvents == 0 {
- return nil
- }
- ctx := with(*info.Context, TRACE, "ydb", "driver", "credentials", "get")
- l.Log(ctx, "start")
- start := time.Now()
+ start := time.Now()
- return func(info trace.DriverGetCredentialsDoneInfo) {
- if info.Error == nil {
+ return func(info trace.DriverBalancerUpdateDoneInfo) {
l.Log(ctx, "done",
latencyField(start),
- String("token", secret.Token(info.Token)),
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "done",
- Error(info.Error),
- latencyField(start),
- String("token", secret.Token(info.Token)),
- versionField(),
+ Stringer("endpoints", endpoints(info.Endpoints)),
+ Stringer("added", endpoints(info.Added)),
+ Stringer("dropped", endpoints(info.Dropped)),
+ String("detectedLocalDC", info.LocalDC),
)
}
- }
- }
+ },
+ OnGetCredentials: func(info trace.DriverGetCredentialsStartInfo) func(trace.DriverGetCredentialsDoneInfo) {
+ if d.Details()&trace.DriverCredentialsEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "driver", "credentials", "get")
+ l.Log(ctx, "start")
+ start := time.Now()
- return t
+ return func(info trace.DriverGetCredentialsDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ String("token", secret.Token(info.Token)),
+ )
+ } else {
+ l.Log(WithLevel(ctx, ERROR), "done",
+ Error(info.Error),
+ latencyField(start),
+ String("token", secret.Token(info.Token)),
+ versionField(),
+ )
+ }
+ }
+ },
+ }
}
diff --git a/log/logger.go b/log/logger.go
index e3981058e..8caf7229a 100644
--- a/log/logger.go
+++ b/log/logger.go
@@ -33,8 +33,10 @@ func Default(w io.Writer, opts ...simpleLoggerOption) *defaultLogger {
clock: clockwork.NewRealClock(),
w: w,
}
- for _, o := range opts {
- o.applySimpleOption(l)
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applySimpleOption(l)
+ }
}
return l
@@ -42,9 +44,9 @@ func Default(w io.Writer, opts ...simpleLoggerOption) *defaultLogger {
type defaultLogger struct {
coloring bool
- clock clockwork.Clock
logQuery bool
minLevel Level
+ clock clockwork.Clock
w io.Writer
}
@@ -104,9 +106,9 @@ func wrapLogger(l Logger, opts ...Option) *wrapper {
ll := &wrapper{
logger: l,
}
- for _, o := range opts {
- if o != nil {
- o.applyHolderOption(ll)
+ for _, opt := range opts {
+ if opt != nil {
+ opt.applyHolderOption(ll)
}
}
diff --git a/log/logger_test.go b/log/logger_test.go
index f7b12a71c..38e9afb75 100644
--- a/log/logger_test.go
+++ b/log/logger_test.go
@@ -9,9 +9,9 @@ import (
)
func TestColoring(t *testing.T) {
- zeroClock := clockwork.NewFakeClock()
- fullDuration := zeroClock.Now().Sub(time.Date(1984, 4, 4, 0, 0, 0, 0, time.UTC))
- zeroClock.Advance(-fullDuration) // set zero time
+ zeroClock := clockwork.NewFakeClockAt(
+ time.Date(1984, 4, 4, 0, 0, 0, 0, time.UTC),
+ )
for _, tt := range []struct {
l *defaultLogger
msg string
diff --git a/log/query.go b/log/query.go
new file mode 100644
index 000000000..e44e9e3ac
--- /dev/null
+++ b/log/query.go
@@ -0,0 +1,653 @@
+package log
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+// Query makes trace.Query with logging events from details
+func Query(l Logger, d trace.Detailer, opts ...Option) (t trace.Query) {
+ return internalQuery(wrapLogger(l, opts...), d)
+}
+
+//nolint:gocyclo
+func internalQuery(
+ l *wrapper, //nolint:interfacer
+ d trace.Detailer,
+) trace.Query {
+ return trace.Query{
+ OnNew: func(info trace.QueryNewStartInfo) func(info trace.QueryNewDoneInfo) {
+ if d.Details()&trace.QueryEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "new")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryNewDoneInfo) {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ )
+ }
+ },
+ OnClose: func(info trace.QueryCloseStartInfo) func(info trace.QueryCloseDoneInfo) {
+ if d.Details()&trace.QueryEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "close")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnPoolNew: func(info trace.QueryPoolNewStartInfo) func(trace.QueryPoolNewDoneInfo) {
+ if d.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "pool", "new")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryPoolNewDoneInfo) {
+ l.Log(WithLevel(ctx, INFO), "done",
+ latencyField(start),
+ Int("Limit", info.Limit),
+ )
+ }
+ },
+ OnPoolClose: func(info trace.QueryPoolCloseStartInfo) func(trace.QueryPoolCloseDoneInfo) {
+ if d.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "pool", "close")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryPoolCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnPoolTry: func(info trace.QueryPoolTryStartInfo) func(trace.QueryPoolTryDoneInfo) {
+ if d.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "pool", "try")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryPoolTryDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnPoolWith: func(info trace.QueryPoolWithStartInfo) func(trace.QueryPoolWithDoneInfo) {
+ if d.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, DEBUG, "ydb", "query", "pool", "with")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryPoolWithDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Int("Attempts", info.Attempts),
+ )
+ } else {
+ lvl := ERROR
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ Int("Attempts", info.Attempts),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnPoolPut: func(info trace.QueryPoolPutStartInfo) func(trace.QueryPoolPutDoneInfo) {
+ if d.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "pool", "put")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryPoolPutDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnPoolGet: func(info trace.QueryPoolGetStartInfo) func(trace.QueryPoolGetDoneInfo) {
+ if d.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "pool", "get")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryPoolGetDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnDo: func(info trace.QueryDoStartInfo) func(trace.QueryDoDoneInfo) {
+ if d.Details()&trace.QueryEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "do")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryDoDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Int("attempts", info.Attempts),
+ )
+ } else {
+ lvl := ERROR
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ Int("attempts", info.Attempts),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnDoTx: func(info trace.QueryDoTxStartInfo) func(trace.QueryDoTxDoneInfo) {
+ if d.Details()&trace.QueryEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "do", "tx")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryDoTxDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ Int("attempts", info.Attempts),
+ )
+ } else {
+ lvl := ERROR
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ Int("attempts", info.Attempts),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnSessionCreate: func(info trace.QuerySessionCreateStartInfo) func(info trace.QuerySessionCreateDoneInfo) {
+ if d.Details()&trace.QuerySessionEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "session", "create")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QuerySessionCreateDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ String("session_id", info.Session.ID()),
+ String("session_status", info.Session.Status()),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "done",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnSessionAttach: func(info trace.QuerySessionAttachStartInfo) func(info trace.QuerySessionAttachDoneInfo) {
+ if d.Details()&trace.QuerySessionEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "session", "attach")
+ l.Log(ctx, "start",
+ String("session_id", info.Session.ID()),
+ String("session_status", info.Session.Status()),
+ )
+ start := time.Now()
+
+ return func(info trace.QuerySessionAttachDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnSessionDelete: func(info trace.QuerySessionDeleteStartInfo) func(info trace.QuerySessionDeleteDoneInfo) {
+ if d.Details()&trace.QuerySessionEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "session", "delete")
+ l.Log(ctx, "start",
+ String("session_id", info.Session.ID()),
+ String("session_status", info.Session.Status()),
+ )
+ start := time.Now()
+
+ return func(info trace.QuerySessionDeleteDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnSessionExecute: func(info trace.QuerySessionExecuteStartInfo) func(info trace.QuerySessionExecuteDoneInfo) {
+ if d.Details()&trace.QuerySessionEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "session", "execute")
+ l.Log(ctx, "start",
+ String("SessionID", info.Session.ID()),
+ String("SessionStatus", info.Session.Status()),
+ String("Query", info.Query),
+ )
+ start := time.Now()
+
+ return func(info trace.QuerySessionExecuteDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnSessionBegin: func(info trace.QuerySessionBeginStartInfo) func(info trace.QuerySessionBeginDoneInfo) {
+ if d.Details()&trace.QuerySessionEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "session", "begin")
+ l.Log(ctx, "start",
+ String("SessionID", info.Session.ID()),
+ String("SessionStatus", info.Session.Status()),
+ )
+ start := time.Now()
+
+ return func(info trace.QuerySessionBeginDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, DEBUG), "done",
+ latencyField(start),
+ String("TransactionID", info.Tx.ID()),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnTxExecute: func(info trace.QueryTxExecuteStartInfo) func(info trace.QueryTxExecuteDoneInfo) {
+ if d.Details()&trace.QueryTransactionEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "transaction", "execute")
+ l.Log(ctx, "start",
+ String("SessionID", info.Session.ID()),
+ String("TransactionID", info.Tx.ID()),
+ String("SessionStatus", info.Session.Status()),
+ )
+ start := time.Now()
+
+ return func(info trace.QueryTxExecuteDoneInfo) {
+ if info.Error == nil {
+ l.Log(WithLevel(ctx, DEBUG), "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnResultNew: func(info trace.QueryResultNewStartInfo) func(info trace.QueryResultNewDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "result", "new")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryResultNewDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnResultNextPart: func(info trace.QueryResultNextPartStartInfo) func(info trace.QueryResultNextPartDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "result", "next", "part")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryResultNextPartDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnResultNextResultSet: func(
+ info trace.QueryResultNextResultSetStartInfo,
+ ) func(
+ info trace.QueryResultNextResultSetDoneInfo,
+ ) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "result", "next", "result", "set")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryResultNextResultSetDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnResultClose: func(info trace.QueryResultCloseStartInfo) func(info trace.QueryResultCloseDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "result", "close")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryResultCloseDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnResultSetNextRow: func(info trace.QueryResultSetNextRowStartInfo) func(info trace.QueryResultSetNextRowDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "result", "set", "next", "row")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryResultSetNextRowDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnRowScan: func(info trace.QueryRowScanStartInfo) func(info trace.QueryRowScanDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "row", "scan")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryRowScanDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnRowScanNamed: func(info trace.QueryRowScanNamedStartInfo) func(info trace.QueryRowScanNamedDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "row", "scan", "named")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryRowScanNamedDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ OnRowScanStruct: func(info trace.QueryRowScanStructStartInfo) func(info trace.QueryRowScanStructDoneInfo) {
+ if d.Details()&trace.QueryResultEvents == 0 {
+ return nil
+ }
+ ctx := with(*info.Context, TRACE, "ydb", "query", "row", "scan", "struct")
+ l.Log(ctx, "start")
+ start := time.Now()
+
+ return func(info trace.QueryRowScanStructDoneInfo) {
+ if info.Error == nil {
+ l.Log(ctx, "done",
+ latencyField(start),
+ )
+ } else {
+ lvl := WARN
+ if !xerrors.IsYdb(info.Error) {
+ lvl = DEBUG
+ }
+ l.Log(WithLevel(ctx, lvl), "failed",
+ latencyField(start),
+ Error(info.Error),
+ versionField(),
+ )
+ }
+ }
+ },
+ }
+}
diff --git a/log/retry.go b/log/retry.go
index 8b52911b7..df772bdb3 100644
--- a/log/retry.go
+++ b/log/retry.go
@@ -14,13 +14,7 @@ func Retry(l Logger, d trace.Detailer, opts ...Option) (t trace.Retry) {
}
func internalRetry(l Logger, d trace.Detailer) (t trace.Retry) {
- t.OnRetry = func(
- info trace.RetryLoopStartInfo,
- ) func(
- trace.RetryLoopIntermediateInfo,
- ) func(
- trace.RetryLoopDoneInfo,
- ) {
+ t.OnRetry = func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
if d.Details()&trace.RetryEvents == 0 {
return nil
}
@@ -33,11 +27,12 @@ func internalRetry(l Logger, d trace.Detailer) (t trace.Retry) {
)
start := time.Now()
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
if info.Error == nil {
- l.Log(ctx, "attempt done",
+ l.Log(ctx, "done",
String("label", label),
latencyField(start),
+ Int("attempts", info.Attempts),
)
} else {
lvl := ERROR
@@ -45,42 +40,17 @@ func internalRetry(l Logger, d trace.Detailer) (t trace.Retry) {
lvl = DEBUG
}
m := retry.Check(info.Error)
- l.Log(WithLevel(ctx, lvl), "attempt failed",
+ l.Log(WithLevel(ctx, lvl), "failed",
Error(info.Error),
String("label", label),
latencyField(start),
+ Int("attempts", info.Attempts),
Bool("retryable", m.MustRetry(idempotent)),
Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
+ Bool("deleteSession", m.IsRetryObjectValid()),
versionField(),
)
}
-
- return func(info trace.RetryLoopDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- String("label", label),
- latencyField(start),
- Int("attempts", info.Attempts),
- )
- } else {
- lvl := ERROR
- if !xerrors.IsYdb(info.Error) {
- lvl = DEBUG
- }
- m := retry.Check(info.Error)
- l.Log(WithLevel(ctx, lvl), "failed",
- Error(info.Error),
- String("label", label),
- latencyField(start),
- Int("attempts", info.Attempts),
- Bool("retryable", m.MustRetry(idempotent)),
- Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
- versionField(),
- )
- }
- }
}
}
diff --git a/log/sql.go b/log/sql.go
index 3a71f3cb7..5ef2a56f3 100644
--- a/log/sql.go
+++ b/log/sql.go
@@ -165,7 +165,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) {
String("query", query),
Bool("retryable", m.MustRetry(idempotent)),
Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
+ Bool("deleteSession", m.IsRetryObjectValid()),
Error(info.Error),
latencyField(start),
versionField(),
@@ -200,7 +200,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) {
String("query", query),
Bool("retryable", m.MustRetry(idempotent)),
Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
+ Bool("deleteSession", m.IsRetryObjectValid()),
Error(info.Error),
latencyField(start),
versionField(),
diff --git a/log/table.go b/log/table.go
index 5afa72bee..c12dd30ad 100644
--- a/log/table.go
+++ b/log/table.go
@@ -18,8 +18,6 @@ func Table(l Logger, d trace.Detailer, opts ...Option) (t trace.Table) {
func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
t.OnDo = func(
info trace.TableDoStartInfo,
- ) func(
- info trace.TableDoIntermediateInfo,
) func(
trace.TableDoDoneInfo,
) {
@@ -35,64 +33,36 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(info trace.TableDoIntermediateInfo) func(trace.TableDoDoneInfo) {
+ return func(info trace.TableDoDoneInfo) {
if info.Error == nil {
l.Log(ctx, "done",
latencyField(start),
Bool("idempotent", idempotent),
String("label", label),
+ Int("attempts", info.Attempts),
)
} else {
- lvl := WARN
+ lvl := ERROR
if !xerrors.IsYdb(info.Error) {
lvl = DEBUG
}
m := retry.Check(info.Error)
- l.Log(WithLevel(ctx, lvl), "failed",
+ l.Log(WithLevel(ctx, lvl), "done",
latencyField(start),
Bool("idempotent", idempotent),
String("label", label),
+ Int("attempts", info.Attempts),
Error(info.Error),
Bool("retryable", m.MustRetry(idempotent)),
Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
+ Bool("deleteSession", m.IsRetryObjectValid()),
versionField(),
)
}
-
- return func(info trace.TableDoDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- latencyField(start),
- Bool("idempotent", idempotent),
- String("label", label),
- Int("attempts", info.Attempts),
- )
- } else {
- lvl := ERROR
- if !xerrors.IsYdb(info.Error) {
- lvl = DEBUG
- }
- m := retry.Check(info.Error)
- l.Log(WithLevel(ctx, lvl), "done",
- latencyField(start),
- Bool("idempotent", idempotent),
- String("label", label),
- Int("attempts", info.Attempts),
- Error(info.Error),
- Bool("retryable", m.MustRetry(idempotent)),
- Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
- versionField(),
- )
- }
- }
}
}
t.OnDoTx = func(
info trace.TableDoTxStartInfo,
- ) func(
- info trace.TableDoTxIntermediateInfo,
) func(
trace.TableDoTxDoneInfo,
) {
@@ -108,15 +78,16 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(info trace.TableDoTxIntermediateInfo) func(trace.TableDoTxDoneInfo) {
+ return func(info trace.TableDoTxDoneInfo) {
if info.Error == nil {
l.Log(ctx, "done",
latencyField(start),
Bool("idempotent", idempotent),
String("label", label),
+ Int("attempts", info.Attempts),
)
} else {
- lvl := ERROR
+ lvl := WARN
if !xerrors.IsYdb(info.Error) {
lvl = DEBUG
}
@@ -125,47 +96,18 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
latencyField(start),
Bool("idempotent", idempotent),
String("label", label),
+ Int("attempts", info.Attempts),
Error(info.Error),
Bool("retryable", m.MustRetry(idempotent)),
Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
+ Bool("deleteSession", m.IsRetryObjectValid()),
versionField(),
)
}
-
- return func(info trace.TableDoTxDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- latencyField(start),
- Bool("idempotent", idempotent),
- String("label", label),
- Int("attempts", info.Attempts),
- )
- } else {
- lvl := WARN
- if !xerrors.IsYdb(info.Error) {
- lvl = DEBUG
- }
- m := retry.Check(info.Error)
- l.Log(WithLevel(ctx, lvl), "done",
- latencyField(start),
- Bool("idempotent", idempotent),
- String("label", label),
- Int("attempts", info.Attempts),
- Error(info.Error),
- Bool("retryable", m.MustRetry(idempotent)),
- Int64("code", m.StatusCode()),
- Bool("deleteSession", m.MustDeleteSession()),
- versionField(),
- )
- }
- }
}
}
t.OnCreateSession = func(
info trace.TableCreateSessionStartInfo,
- ) func(
- info trace.TableCreateSessionIntermediateInfo,
) func(
trace.TableCreateSessionDoneInfo,
) {
@@ -176,36 +118,22 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
l.Log(ctx, "start")
start := time.Now()
- return func(info trace.TableCreateSessionIntermediateInfo) func(trace.TableCreateSessionDoneInfo) {
+ return func(info trace.TableCreateSessionDoneInfo) {
if info.Error == nil {
- l.Log(ctx, "intermediate",
+ l.Log(ctx, "done",
latencyField(start),
+ Int("attempts", info.Attempts),
+ String("session_id", info.Session.ID()),
+ String("session_status", info.Session.Status()),
)
} else {
- l.Log(WithLevel(ctx, ERROR), "intermediate",
+ l.Log(WithLevel(ctx, ERROR), "failed",
latencyField(start),
+ Int("attempts", info.Attempts),
Error(info.Error),
versionField(),
)
}
-
- return func(info trace.TableCreateSessionDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- latencyField(start),
- Int("attempts", info.Attempts),
- String("session_id", info.Session.ID()),
- String("session_status", info.Session.Status()),
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "failed",
- latencyField(start),
- Int("attempts", info.Attempts),
- Error(info.Error),
- versionField(),
- )
- }
- }
}
}
t.OnSessionNew = func(info trace.TableSessionNewStartInfo) func(trace.TableSessionNewDoneInfo) {
@@ -396,8 +324,6 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
}
t.OnSessionQueryStreamExecute = func(
info trace.TableSessionQueryStreamExecuteStartInfo,
- ) func(
- trace.TableSessionQueryStreamExecuteIntermediateInfo,
) func(
trace.TableSessionQueryStreamExecuteDoneInfo,
) {
@@ -416,50 +342,33 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(
- info trace.TableSessionQueryStreamExecuteIntermediateInfo,
- ) func(
- trace.TableSessionQueryStreamExecuteDoneInfo,
- ) {
+ return func(info trace.TableSessionQueryStreamExecuteDoneInfo) {
if info.Error == nil {
- l.Log(ctx, "intermediate")
+ l.Log(ctx, "done",
+ appendFieldByCondition(l.logQuery,
+ Stringer("query", query),
+ Error(info.Error),
+ String("id", session.ID()),
+ String("status", session.Status()),
+ latencyField(start),
+ )...,
+ )
} else {
- l.Log(WithLevel(ctx, WARN), "failed",
- Error(info.Error),
- versionField(),
+ l.Log(WithLevel(ctx, ERROR), "failed",
+ appendFieldByCondition(l.logQuery,
+ Stringer("query", query),
+ Error(info.Error),
+ String("id", session.ID()),
+ String("status", session.Status()),
+ latencyField(start),
+ versionField(),
+ )...,
)
}
-
- return func(info trace.TableSessionQueryStreamExecuteDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- appendFieldByCondition(l.logQuery,
- Stringer("query", query),
- Error(info.Error),
- String("id", session.ID()),
- String("status", session.Status()),
- latencyField(start),
- )...,
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "failed",
- appendFieldByCondition(l.logQuery,
- Stringer("query", query),
- Error(info.Error),
- String("id", session.ID()),
- String("status", session.Status()),
- latencyField(start),
- versionField(),
- )...,
- )
- }
- }
}
}
t.OnSessionQueryStreamRead = func(
info trace.TableSessionQueryStreamReadStartInfo,
- ) func(
- intermediateInfo trace.TableSessionQueryStreamReadIntermediateInfo,
) func(
trace.TableSessionQueryStreamReadDoneInfo,
) {
@@ -474,43 +383,28 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(
- info trace.TableSessionQueryStreamReadIntermediateInfo,
- ) func(
- trace.TableSessionQueryStreamReadDoneInfo,
- ) {
+ return func(info trace.TableSessionQueryStreamReadDoneInfo) {
if info.Error == nil {
- l.Log(ctx, "intermediate")
+ l.Log(ctx, "done",
+ latencyField(start),
+ String("id", session.ID()),
+ String("status", session.Status()),
+ )
} else {
- l.Log(WithLevel(ctx, WARN), "failed",
+ l.Log(WithLevel(ctx, ERROR), "failed",
+ latencyField(start),
+ String("id", session.ID()),
+ String("status", session.Status()),
Error(info.Error),
versionField(),
)
}
-
- return func(info trace.TableSessionQueryStreamReadDoneInfo) {
- if info.Error == nil {
- l.Log(ctx, "done",
- latencyField(start),
- String("id", session.ID()),
- String("status", session.Status()),
- )
- } else {
- l.Log(WithLevel(ctx, ERROR), "failed",
- latencyField(start),
- String("id", session.ID()),
- String("status", session.Status()),
- Error(info.Error),
- versionField(),
- )
- }
- }
}
}
- t.OnSessionTransactionBegin = func(
- info trace.TableSessionTransactionBeginStartInfo,
+ t.OnTxBegin = func(
+ info trace.TableTxBeginStartInfo,
) func(
- trace.TableSessionTransactionBeginDoneInfo,
+ trace.TableTxBeginDoneInfo,
) {
if d.Details()&trace.TableSessionTransactionEvents == 0 {
return nil
@@ -523,7 +417,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(info trace.TableSessionTransactionBeginDoneInfo) {
+ return func(info trace.TableTxBeginDoneInfo) {
if info.Error == nil {
l.Log(ctx, "done",
latencyField(start),
@@ -542,11 +436,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
}
}
}
- t.OnSessionTransactionCommit = func(
- info trace.TableSessionTransactionCommitStartInfo,
- ) func(
- trace.TableSessionTransactionCommitDoneInfo,
- ) {
+ t.OnTxCommit = func(info trace.TableTxCommitStartInfo) func(trace.TableTxCommitDoneInfo) {
if d.Details()&trace.TableSessionTransactionEvents == 0 {
return nil
}
@@ -560,7 +450,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(info trace.TableSessionTransactionCommitDoneInfo) {
+ return func(info trace.TableTxCommitDoneInfo) {
if info.Error == nil {
l.Log(ctx, "done",
latencyField(start),
@@ -580,10 +470,10 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
}
}
}
- t.OnSessionTransactionRollback = func(
- info trace.TableSessionTransactionRollbackStartInfo,
+ t.OnTxRollback = func(
+ info trace.TableTxRollbackStartInfo,
) func(
- trace.TableSessionTransactionRollbackDoneInfo,
+ trace.TableTxRollbackDoneInfo,
) {
if d.Details()&trace.TableSessionTransactionEvents == 0 {
return nil
@@ -598,7 +488,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) {
)
start := time.Now()
- return func(info trace.TableSessionTransactionRollbackDoneInfo) {
+ return func(info trace.TableTxRollbackDoneInfo) {
if info.Error == nil {
l.Log(ctx, "done",
latencyField(start),
diff --git a/meta/context.go b/meta/context.go
index b2f95b5be..5ea200db6 100644
--- a/meta/context.go
+++ b/meta/context.go
@@ -14,8 +14,15 @@ func WithTraceID(ctx context.Context, traceID string) context.Context {
}
// WithUserAgent returns a copy of parent context with custom user-agent info
-func WithUserAgent(ctx context.Context, userAgent string) context.Context {
- return meta.WithUserAgent(ctx, userAgent)
+//
+// Deprecated: use WithApplicationName instead
+func WithUserAgent(ctx context.Context, _ string) context.Context {
+ return ctx
+}
+
+// WithApplicationName returns a copy of parent context with application name
+func WithApplicationName(ctx context.Context, applicationName string) context.Context {
+ return meta.WithApplicationName(ctx, applicationName)
}
// WithRequestType returns a copy of parent context with custom request type
@@ -25,7 +32,7 @@ func WithRequestType(ctx context.Context, requestType string) context.Context {
// WithAllowFeatures returns a copy of parent context with allowed client feature
func WithAllowFeatures(ctx context.Context, features ...string) context.Context {
- return meta.WithAllowFeatures(ctx, features)
+ return meta.WithAllowFeatures(ctx, features...)
}
// WithTrailerCallback attaches callback to context for listening incoming metadata
diff --git a/metrics/driver.go b/metrics/driver.go
index f20e7ae09..5b2f12fbc 100644
--- a/metrics/driver.go
+++ b/metrics/driver.go
@@ -47,8 +47,6 @@ func driver(config Config) (t trace.Driver) {
}
}
t.OnConnNewStream = func(info trace.DriverConnNewStreamStartInfo) func(
- trace.DriverConnNewStreamRecvInfo,
- ) func(
trace.DriverConnNewStreamDoneInfo,
) {
var (
@@ -57,16 +55,14 @@ func driver(config Config) (t trace.Driver) {
nodeID = info.Endpoint.NodeID()
)
- return func(info trace.DriverConnNewStreamRecvInfo) func(trace.DriverConnNewStreamDoneInfo) {
- return func(info trace.DriverConnNewStreamDoneInfo) {
- if config.Details()&trace.DriverConnEvents != 0 {
- requests.With(map[string]string{
- "status": errorBrief(info.Error),
- "method": string(method),
- "endpoint": endpoint,
- "node_id": strconv.FormatUint(uint64(nodeID), 10),
- }).Inc()
- }
+ return func(info trace.DriverConnNewStreamDoneInfo) {
+ if config.Details()&trace.DriverConnEvents != 0 {
+ requests.With(map[string]string{
+ "status": errorBrief(info.Error),
+ "method": string(method),
+ "endpoint": endpoint,
+ "node_id": strconv.FormatUint(uint64(nodeID), 10),
+ }).Inc()
}
}
}
diff --git a/metrics/query.go b/metrics/query.go
new file mode 100644
index 000000000..2d48cfd52
--- /dev/null
+++ b/metrics/query.go
@@ -0,0 +1,210 @@
+package metrics
+
+import (
+ "time"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+func query(config Config) (t trace.Query) {
+ queryConfig := config.WithSystem("query")
+ {
+ poolConfig := queryConfig.WithSystem("pool")
+ {
+ withConfig := poolConfig.WithSystem("with")
+ errs := withConfig.CounterVec("errs", "status")
+ latency := withConfig.TimerVec("latency")
+ attempts := withConfig.HistogramVec("attempts", []float64{0, 1, 2, 3, 4, 5, 7, 10})
+ t.OnPoolWith = func(
+ info trace.QueryPoolWithStartInfo,
+ ) func(
+ info trace.QueryPoolWithDoneInfo,
+ ) {
+ if withConfig.Details()&trace.QueryPoolEvents == 0 {
+ return nil
+ }
+
+ start := time.Now()
+
+ return func(info trace.QueryPoolWithDoneInfo) {
+ attempts.With(nil).Record(float64(info.Attempts))
+ if info.Error != nil {
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ }
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ {
+ sizeConfig := poolConfig.WithSystem("size")
+ limit := sizeConfig.GaugeVec("limit")
+ idle := sizeConfig.GaugeVec("idle")
+ index := sizeConfig.GaugeVec("index")
+ inUse := sizeConfig.WithSystem("in").GaugeVec("use")
+
+ t.OnPoolChange = func(stats trace.QueryPoolChange) {
+ if sizeConfig.Details()&trace.QueryPoolEvents == 0 {
+ return
+ }
+
+ limit.With(nil).Set(float64(stats.Limit))
+ idle.With(nil).Set(float64(stats.Idle))
+ inUse.With(nil).Set(float64(stats.InUse))
+ index.With(nil).Set(float64(stats.Index))
+ }
+ }
+ }
+ {
+ doConfig := queryConfig.WithSystem("do")
+ {
+ errs := doConfig.CounterVec("errs", "status")
+ attempts := doConfig.HistogramVec("attempts", []float64{0, 1, 2, 3, 4, 5, 7, 10})
+ latency := doConfig.TimerVec("latency")
+ t.OnDo = func(
+ info trace.QueryDoStartInfo,
+ ) func(
+ trace.QueryDoDoneInfo,
+ ) {
+ start := time.Now()
+
+ return func(info trace.QueryDoDoneInfo) {
+ if doConfig.Details()&trace.QueryEvents != 0 {
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ attempts.With(nil).Record(float64(info.Attempts))
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ }
+ {
+ doTxConfig := doConfig.WithSystem("tx")
+ errs := doTxConfig.CounterVec("errs", "status")
+ attempts := doTxConfig.HistogramVec("attempts", []float64{0, 1, 2, 3, 4, 5, 7, 10})
+ latency := doTxConfig.TimerVec("latency")
+ t.OnDoTx = func(
+ info trace.QueryDoTxStartInfo,
+ ) func(
+ trace.QueryDoTxDoneInfo,
+ ) {
+ start := time.Now()
+
+ return func(info trace.QueryDoTxDoneInfo) {
+ if doTxConfig.Details()&trace.QueryEvents != 0 {
+ attempts.With(nil).Record(float64(info.Attempts))
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ }
+ }
+ {
+ sessionConfig := queryConfig.WithSystem("session")
+ count := sessionConfig.GaugeVec("count")
+ {
+ createConfig := sessionConfig.WithSystem("create")
+ errs := createConfig.CounterVec("errs", "status")
+ latency := createConfig.TimerVec("latency")
+ t.OnSessionCreate = func(
+ info trace.QuerySessionCreateStartInfo,
+ ) func(
+ info trace.QuerySessionCreateDoneInfo,
+ ) {
+ start := time.Now()
+
+ return func(info trace.QuerySessionCreateDoneInfo) {
+ if createConfig.Details()&trace.QuerySessionEvents != 0 {
+ if info.Error == nil {
+ count.With(nil).Add(1)
+ }
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ }
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ {
+ deleteConfig := sessionConfig.WithSystem("delete")
+ errs := deleteConfig.CounterVec("errs", "status")
+ latency := deleteConfig.TimerVec("latency")
+ t.OnSessionDelete = func(info trace.QuerySessionDeleteStartInfo) func(info trace.QuerySessionDeleteDoneInfo) {
+ count.With(nil).Add(-1)
+
+ start := time.Now()
+
+ return func(info trace.QuerySessionDeleteDoneInfo) {
+ if deleteConfig.Details()&trace.QuerySessionEvents != 0 {
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ }
+ {
+ executeConfig := sessionConfig.WithSystem("execute")
+ errs := executeConfig.CounterVec("errs", "status")
+ latency := executeConfig.TimerVec("latency")
+ t.OnSessionExecute = func(info trace.QuerySessionExecuteStartInfo) func(info trace.QuerySessionExecuteDoneInfo) {
+ start := time.Now()
+
+ return func(info trace.QuerySessionExecuteDoneInfo) {
+ if executeConfig.Details()&trace.QuerySessionEvents != 0 {
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ }
+ {
+ beginConfig := sessionConfig.WithSystem("begin")
+ errs := beginConfig.CounterVec("errs", "status")
+ latency := beginConfig.TimerVec("latency")
+ t.OnSessionBegin = func(info trace.QuerySessionBeginStartInfo) func(info trace.QuerySessionBeginDoneInfo) {
+ start := time.Now()
+
+ return func(info trace.QuerySessionBeginDoneInfo) {
+ if beginConfig.Details()&trace.QuerySessionEvents != 0 {
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ }
+ }
+ {
+ txConfig := queryConfig.WithSystem("tx")
+ {
+ executeConfig := txConfig.WithSystem("execute")
+ errs := executeConfig.CounterVec("errs", "status")
+ latency := executeConfig.TimerVec("latency")
+ t.OnTxExecute = func(info trace.QueryTxExecuteStartInfo) func(info trace.QueryTxExecuteDoneInfo) {
+ start := time.Now()
+
+ return func(info trace.QueryTxExecuteDoneInfo) {
+ if executeConfig.Details()&trace.QuerySessionEvents != 0 {
+ errs.With(map[string]string{
+ "status": errorBrief(info.Error),
+ }).Inc()
+ latency.With(nil).Record(time.Since(start))
+ }
+ }
+ }
+ }
+ }
+
+ return t
+}
diff --git a/metrics/retry.go b/metrics/retry.go
index 35b0fe9a0..994fd493e 100644
--- a/metrics/retry.go
+++ b/metrics/retry.go
@@ -11,36 +11,26 @@ func retry(config Config) (t trace.Retry) {
errs := config.CounterVec("errors", "status", "retry_label", "final")
attempts := config.HistogramVec("attempts", []float64{0, 1, 2, 3, 4, 5, 7, 10}, "retry_label")
latency := config.TimerVec("latency", "retry_label")
- t.OnRetry = func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
+ t.OnRetry = func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
label := info.Label
if label == "" {
return nil
}
start := time.Now()
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- if info.Error != nil && config.Details()&trace.RetryEvents != 0 {
+ return func(info trace.RetryLoopDoneInfo) {
+ if config.Details()&trace.RetryEvents != 0 {
+ attempts.With(map[string]string{
+ "retry_label": label,
+ }).Record(float64(info.Attempts))
errs.With(map[string]string{
"status": errorBrief(info.Error),
"retry_label": label,
- "final": "false",
+ "final": "true",
}).Inc()
- }
-
- return func(info trace.RetryLoopDoneInfo) {
- if config.Details()&trace.RetryEvents != 0 {
- attempts.With(map[string]string{
- "retry_label": label,
- }).Record(float64(info.Attempts))
- errs.With(map[string]string{
- "status": errorBrief(info.Error),
- "retry_label": label,
- "final": "true",
- }).Inc()
- latency.With(map[string]string{
- "retry_label": label,
- }).Record(time.Since(start))
- }
+ latency.With(map[string]string{
+ "retry_label": label,
+ }).Record(time.Since(start))
}
}
}
diff --git a/metrics/traces.go b/metrics/traces.go
index 1feaab9a3..7744ebbcb 100644
--- a/metrics/traces.go
+++ b/metrics/traces.go
@@ -13,6 +13,7 @@ func WithTraces(config Config) ydb.Option {
return ydb.MergeOptions(
ydb.WithTraceDriver(driver(config)),
ydb.WithTraceTable(table(config)),
+ ydb.WithTraceQuery(query(config)),
ydb.WithTraceScripting(scripting(config)),
ydb.WithTraceScheme(scheme(config)),
ydb.WithTraceCoordination(coordination(config)),
diff --git a/options.go b/options.go
index 41c995f97..23d0ef1ee 100644
--- a/options.go
+++ b/options.go
@@ -17,6 +17,7 @@ import (
coordinationConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination/config"
discoveryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/discovery/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/dsn"
+ queryConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/config"
ratelimiterConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/config"
schemeConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scheme/config"
scriptingConfig "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting/config"
@@ -53,10 +54,21 @@ func WithAccessTokenCredentials(accessToken string) Option {
)
}
+// WithApplicationName add provided application name to all api requests
+func WithApplicationName(applicationName string) Option {
+ return func(ctx context.Context, c *Driver) error {
+ c.options = append(c.options, config.WithApplicationName(applicationName))
+
+ return nil
+ }
+}
+
// WithUserAgent add provided user agent value to all api requests
+//
+// Deprecated: use WithApplicationName instead
func WithUserAgent(userAgent string) Option {
return func(ctx context.Context, c *Driver) error {
- c.options = append(c.options, config.WithUserAgent(userAgent))
+ c.options = append(c.options, config.WithApplicationName(userAgent))
return nil
}
@@ -246,9 +258,9 @@ func With(options ...config.Option) Option {
// MergeOptions concatentaes provided options to one cumulative value.
func MergeOptions(opts ...Option) Option {
return func(ctx context.Context, c *Driver) error {
- for _, o := range opts {
- if o != nil {
- if err := o(ctx, c); err != nil {
+ for _, opt := range opts {
+ if opt != nil {
+ if err := opt(ctx, c); err != nil {
return xerrors.WithStackTrace(err)
}
}
@@ -367,22 +379,26 @@ func WithTableConfigOption(option tableConfig.Option) Option {
}
}
+// WithQueryConfigOption collects additional configuration options for query.Client.
+// This option does not replace collected option, instead it will appen provided options.
+func WithQueryConfigOption(option queryConfig.Option) Option {
+ return func(ctx context.Context, c *Driver) error {
+ c.queryOptions = append(c.queryOptions, option)
+
+ return nil
+ }
+}
+
// WithSessionPoolSizeLimit set max size of internal sessions pool in table.Client
func WithSessionPoolSizeLimit(sizeLimit int) Option {
return func(ctx context.Context, c *Driver) error {
c.tableOptions = append(c.tableOptions, tableConfig.WithSizeLimit(sizeLimit))
+ c.queryOptions = append(c.queryOptions, queryConfig.WithPoolLimit(sizeLimit))
return nil
}
}
-// WithSessionPoolKeepAliveMinSize set minimum sessions should be keeped alive in table.Client
-//
-// Deprecated: table client do not supports background session keep-aliving now
-func WithSessionPoolKeepAliveMinSize(keepAliveMinSize int) Option {
- return func(ctx context.Context, c *Driver) error { return nil }
-}
-
// WithSessionPoolIdleThreshold defines interval for idle sessions
func WithSessionPoolIdleThreshold(idleThreshold time.Duration) Option {
return func(ctx context.Context, c *Driver) error {
@@ -396,15 +412,11 @@ func WithSessionPoolIdleThreshold(idleThreshold time.Duration) Option {
}
}
-// WithSessionPoolKeepAliveTimeout set timeout of keep alive requests for session in table.Client
-func WithSessionPoolKeepAliveTimeout(keepAliveTimeout time.Duration) Option {
- return func(ctx context.Context, c *Driver) error { return nil }
-}
-
// WithSessionPoolCreateSessionTimeout set timeout for new session creation process in table.Client
func WithSessionPoolCreateSessionTimeout(createSessionTimeout time.Duration) Option {
return func(ctx context.Context, c *Driver) error {
c.tableOptions = append(c.tableOptions, tableConfig.WithCreateSessionTimeout(createSessionTimeout))
+ c.queryOptions = append(c.queryOptions, queryConfig.WithSessionCreateTimeout(createSessionTimeout))
return nil
}
@@ -414,11 +426,26 @@ func WithSessionPoolCreateSessionTimeout(createSessionTimeout time.Duration) Opt
func WithSessionPoolDeleteTimeout(deleteTimeout time.Duration) Option {
return func(ctx context.Context, c *Driver) error {
c.tableOptions = append(c.tableOptions, tableConfig.WithDeleteTimeout(deleteTimeout))
+ c.queryOptions = append(c.queryOptions, queryConfig.WithSessionDeleteTimeout(deleteTimeout))
return nil
}
}
+// WithSessionPoolKeepAliveMinSize set minimum sessions should be keeped alive in table.Client
+//
+// Deprecated: table client do not support background session keep-aliving now
+func WithSessionPoolKeepAliveMinSize(keepAliveMinSize int) Option {
+ return func(ctx context.Context, c *Driver) error { return nil }
+}
+
+// WithSessionPoolKeepAliveTimeout set timeout of keep alive requests for session in table.Client
+//
+// Deprecated: table client do not support background session keep-aliving now
+func WithSessionPoolKeepAliveTimeout(keepAliveTimeout time.Duration) Option {
+ return func(ctx context.Context, c *Driver) error { return nil }
+}
+
// WithIgnoreTruncated disables errors on truncated flag
func WithIgnoreTruncated() Option {
return func(ctx context.Context, c *Driver) error {
@@ -461,6 +488,25 @@ func WithTraceTable(t trace.Table, opts ...trace.TableComposeOption) Option { //
}
}
+// WithTraceQuery appends trace.Query into query traces
+func WithTraceQuery(t trace.Query, opts ...trace.QueryComposeOption) Option { //nolint:gocritic
+ return func(ctx context.Context, c *Driver) error {
+ c.queryOptions = append(
+ c.queryOptions,
+ queryConfig.WithTrace(&t,
+ append(
+ []trace.QueryComposeOption{
+ trace.WithQueryPanicCallback(c.panicCallback),
+ },
+ opts...,
+ )...,
+ ),
+ )
+
+ return nil
+ }
+}
+
// WithTraceScripting scripting trace option
func WithTraceScripting(t trace.Scripting, opts ...trace.ScriptingComposeOption) Option {
return func(ctx context.Context, c *Driver) error {
@@ -502,12 +548,12 @@ func WithTraceScheme(t trace.Scheme, opts ...trace.SchemeComposeOption) Option {
}
// WithTraceCoordination returns coordination trace option
-func WithTraceCoordination(t trace.Coordination, opts ...trace.CoordinationComposeOption) Option {
+func WithTraceCoordination(t trace.Coordination, opts ...trace.CoordinationComposeOption) Option { //nolint:gocritic
return func(ctx context.Context, c *Driver) error {
c.coordinationOptions = append(
c.coordinationOptions,
coordinationConfig.WithTrace(
- t,
+ &t,
append(
[]trace.CoordinationComposeOption{
trace.WithCoordinationPanicCallback(c.panicCallback),
diff --git a/params_builder.go b/params_builder.go
new file mode 100644
index 000000000..e9e3f5dfb
--- /dev/null
+++ b/params_builder.go
@@ -0,0 +1,12 @@
+package ydb
+
+import "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+
+// ParamsBuilder used for create query arguments instead of tons options.
+//
+// # Experimental
+//
+// Notice: This API is EXPERIMENTAL and may be changed or removed in a later release.
+func ParamsBuilder() params.Builder {
+ return params.Builder{}
+}
diff --git a/query/client.go b/query/client.go
new file mode 100644
index 000000000..87fedc52b
--- /dev/null
+++ b/query/client.go
@@ -0,0 +1,68 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/closer"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+type Client interface {
+ // Do provide the best effort for execute operation.
+ //
+ // Do implements internal busy loop until one of the following conditions is met:
+ // - deadline was canceled or deadlined
+ // - retry operation returned nil as error
+ //
+ // Warning: if context without deadline or cancellation func than Do can run indefinitely.
+ Do(ctx context.Context, op Operation, opts ...options.DoOption) error
+
+ // DoTx provide the best effort for execute transaction.
+ //
+ // DoTx implements internal busy loop until one of the following conditions is met:
+ // - deadline was canceled or deadlined
+ // - retry operation returned nil as error
+ //
+ // DoTx makes auto selector (with TransactionSettings, by default - SerializableReadWrite), commit and
+ // rollback (on error) of transaction.
+ //
+ // If op TxOperation returns nil - transaction will be committed
+ // If op TxOperation return non nil - transaction will be rollback
+ // Warning: if context without deadline or cancellation func than DoTx can run indefinitely
+ DoTx(ctx context.Context, op TxOperation, opts ...options.DoTxOption) error
+}
+
+type (
+ // Operation is the interface that holds an operation for retry.
+ // if Operation returns not nil - operation will retry
+ // if Operation returns nil - retry loop will break
+ Operation func(ctx context.Context, s Session) error
+
+ // TxOperation is the interface that holds an operation for retry.
+ // if TxOperation returns not nil - operation will retry
+ // if TxOperation returns nil - retry loop will break
+ TxOperation func(ctx context.Context, tx TxActor) error
+
+ ClosableSession interface {
+ closer.Closer
+
+ Session
+ }
+ bothDoAndDoTxOption interface {
+ options.DoOption
+ options.DoTxOption
+ }
+)
+
+func WithIdempotent() bothDoAndDoTxOption {
+ return options.WithIdempotent()
+}
+
+func WithTrace(t *trace.Query) bothDoAndDoTxOption {
+ return options.WithTrace(t)
+}
+
+func WithLabel(lbl string) bothDoAndDoTxOption {
+ return options.WithLabel(lbl)
+}
diff --git a/query/example_test.go b/query/example_test.go
new file mode 100644
index 000000000..c0dec4c30
--- /dev/null
+++ b/query/example_test.go
@@ -0,0 +1,198 @@
+package query_test
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+)
+
+func Example_selectWithoutParameters() {
+ ctx := context.TODO()
+ db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
+ if err != nil {
+ fmt.Printf("failed connect: %v", err)
+
+ return
+ }
+ defer db.Close(ctx) // cleanup resources
+ var (
+ id int32 // required value
+ myStr string // optional value
+ )
+ // Do retry operation on errors with best effort
+ err = db.Query().Do(ctx, // context manage exiting from Do
+ func(ctx context.Context, s query.Session) (err error) { // retry operation
+ _, res, err := s.Execute(ctx,
+ `SELECT 42 as id, "my string" as myStr`,
+ )
+ if err != nil {
+ return err // for auto-retry with driver
+ }
+ defer func() { _ = res.Close(ctx) }() // cleanup resources
+ for { // iterate over result sets
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ for { // iterate over rows
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ if err = row.Scan(&id, &myStr); err != nil {
+ return err // generally scan error not retryable, return it for driver check error
+ }
+ }
+ }
+
+ return res.Err() // return finally result error for auto-retry with driver
+ },
+ query.WithIdempotent(),
+ )
+ if err != nil {
+ fmt.Printf("unexpected error: %v", err)
+ }
+ fmt.Printf("id=%v, myStr='%s'\n", id, myStr)
+}
+
+func Example_selectWithParameters() {
+ ctx := context.TODO()
+ db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
+ if err != nil {
+ fmt.Printf("failed connect: %v", err)
+
+ return
+ }
+ defer db.Close(ctx) // cleanup resources
+ var (
+ id int32 // required value
+ myStr string // optional value
+ )
+ // Do retry operation on errors with best effort
+ err = db.Query().Do(ctx, // context manage exiting from Do
+ func(ctx context.Context, s query.Session) (err error) { // retry operation
+ _, res, err := s.Execute(ctx,
+ `SELECT CAST($id AS Uint64) AS id, CAST($myStr AS Text) AS myStr`,
+ options.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$id").Uint64(123).
+ Param("$myStr").Text("123").
+ Build(),
+ ),
+ )
+ if err != nil {
+ return err // for auto-retry with driver
+ }
+ defer func() { _ = res.Close(ctx) }() // cleanup resources
+ for { // iterate over result sets
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ for { // iterate over rows
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ if err = row.ScanNamed(
+ query.Named("id", &id),
+ query.Named("myStr", &myStr),
+ ); err != nil {
+ return err // generally scan error not retryable, return it for driver check error
+ }
+ }
+ }
+
+ return res.Err() // return finally result error for auto-retry with driver
+ },
+ options.WithIdempotent(),
+ )
+ if err != nil {
+ fmt.Printf("unexpected error: %v", err)
+ }
+ fmt.Printf("id=%v, myStr='%s'\n", id, myStr)
+}
+
+func Example_txSelect() {
+ ctx := context.TODO()
+ db, err := ydb.Open(ctx, "grpc://localhost:2136/local")
+ if err != nil {
+ fmt.Printf("failed connect: %v", err)
+
+ return
+ }
+ defer db.Close(ctx) // cleanup resources
+ var (
+ id int32 // required value
+ myStr string // optional value
+ )
+ // Do retry operation on errors with best effort
+ err = db.Query().DoTx(ctx, // context manage exiting from Do
+ func(ctx context.Context, tx query.TxActor) (err error) { // retry operation
+ res, err := tx.Execute(ctx,
+ `SELECT 42 as id, "my string" as myStr`,
+ )
+ if err != nil {
+ return err // for auto-retry with driver
+ }
+ defer func() { _ = res.Close(ctx) }() // cleanup resources
+ for { // iterate over result sets
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ for { // iterate over rows
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
+
+ return err
+ }
+ if err = row.ScanNamed(
+ query.Named("id", &id),
+ query.Named("myStr", &myStr),
+ ); err != nil {
+ return err // generally scan error not retryable, return it for driver check error
+ }
+ }
+ }
+
+ return res.Err() // return finally result error for auto-retry with driver
+ },
+ options.WithIdempotent(),
+ options.WithTxSettings(query.TxSettings(
+ query.WithSnapshotReadOnly(),
+ )),
+ )
+ if err != nil {
+ fmt.Printf("unexpected error: %v", err)
+ }
+ fmt.Printf("id=%v, myStr='%s'\n", id, myStr)
+}
diff --git a/query/result.go b/query/result.go
new file mode 100644
index 000000000..3cef8e9ee
--- /dev/null
+++ b/query/result.go
@@ -0,0 +1,41 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/closer"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/scanner"
+)
+
+type (
+ Result interface {
+ closer.Closer
+
+ NextResultSet(ctx context.Context) (ResultSet, error)
+ Err() error
+ }
+ ResultSet interface {
+ NextRow(ctx context.Context) (Row, error)
+ }
+ Row interface {
+ Scan(dst ...interface{}) error
+ ScanNamed(dst ...scanner.NamedDestination) error
+ ScanStruct(dst interface{}, opts ...scanner.ScanStructOption) error
+ }
+)
+
+func Named(columnName string, destinationValueReference interface{}) (dst scanner.NamedDestination) {
+ return scanner.NamedRef(columnName, destinationValueReference)
+}
+
+func WithScanStructTagName(name string) scanner.ScanStructOption {
+ return scanner.WithTagName(name)
+}
+
+func WithScanStructAllowMissingColumnsFromSelect() scanner.ScanStructOption {
+ return scanner.WithAllowMissingColumnsFromSelect()
+}
+
+func WithScanStructAllowMissingFieldsInStruct() scanner.ScanStructOption {
+ return scanner.WithAllowMissingFieldsInStruct()
+}
diff --git a/query/session.go b/query/session.go
new file mode 100644
index 000000000..14da2f3d5
--- /dev/null
+++ b/query/session.go
@@ -0,0 +1,83 @@
+package query
+
+import (
+ "context"
+
+ "google.golang.org/grpc"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/tx"
+)
+
+type (
+ SessionInfo interface {
+ ID() string
+ NodeID() int64
+ Status() string
+ }
+
+ Session interface {
+ SessionInfo
+
+ // Execute executes query.
+ //
+ // Execute used by default:
+ // - DefaultTxControl (NoTx)
+ // - flag WithKeepInCache(true) if params is not empty.
+ Execute(ctx context.Context, query string, opts ...options.ExecuteOption) (tx Transaction, r Result, err error)
+
+ Begin(ctx context.Context, txSettings TransactionSettings) (Transaction, error)
+ }
+)
+
+const (
+ SyntaxYQL = options.SyntaxYQL
+ SyntaxPostgreSQL = options.SyntaxPostgreSQL
+)
+
+const (
+ ExecModeParse = options.ExecModeParse
+ ExecModeValidate = options.ExecModeValidate
+ ExecModeExplain = options.ExecModeExplain
+ ExecModeExecute = options.ExecModeExecute
+)
+
+const (
+ StatsModeBasic = options.StatsModeBasic
+ StatsModeNone = options.StatsModeNone
+ StatsModeFull = options.StatsModeFull
+ StatsModeProfile = options.StatsModeProfile
+)
+
+func WithParameters(parameters *params.Parameters) options.ParametersOption {
+ return options.WithParameters(parameters)
+}
+
+func WithTxControl(txControl *tx.Control) options.TxControlOption {
+ return options.WithTxControl(txControl)
+}
+
+func WithTxSettings(txSettings tx.Settings) options.DoTxOption {
+ return options.WithTxSettings(txSettings)
+}
+
+func WithCommit() options.TxExecuteOption {
+ return options.WithCommit()
+}
+
+func WithExecMode(mode options.ExecMode) options.ExecModeOption {
+ return options.WithExecMode(mode)
+}
+
+func WithSyntax(syntax options.Syntax) options.SyntaxOption {
+ return options.WithSyntax(syntax)
+}
+
+func WithStatsMode(mode options.StatsMode) options.StatsModeOption {
+ return options.WithStatsMode(mode)
+}
+
+func WithCallOptions(opts ...grpc.CallOption) options.CallOptions {
+ return options.WithCallOptions(opts...)
+}
diff --git a/query/stats.go b/query/stats.go
new file mode 100644
index 000000000..f93b8867c
--- /dev/null
+++ b/query/stats.go
@@ -0,0 +1,18 @@
+package query
+
+import (
+ "fmt"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/pool/stats"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+)
+
+func Stats(client Client) (*stats.Stats, error) {
+ if c, has := client.(interface {
+ Stats() *stats.Stats
+ }); has {
+ return c.Stats(), nil
+ }
+
+ return nil, xerrors.WithStackTrace(fmt.Errorf("client %T not supported stats", client))
+}
diff --git a/query/transaction.go b/query/transaction.go
new file mode 100644
index 000000000..877c93296
--- /dev/null
+++ b/query/transaction.go
@@ -0,0 +1,125 @@
+package query
+
+import (
+ "context"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/tx"
+)
+
+type (
+ TxIdentifier interface {
+ ID() string
+ }
+ TxActor interface {
+ TxIdentifier
+
+ // Execute executes query.
+ //
+ // Execute used by default:
+ // - DefaultTxControl
+ // - flag WithKeepInCache(true) if params is not empty.
+ Execute(ctx context.Context, query string, opts ...options.TxExecuteOption) (r Result, err error)
+ }
+ Transaction interface {
+ TxActor
+
+ CommitTx(ctx context.Context) (err error)
+ Rollback(ctx context.Context) (err error)
+ }
+ TransactionControl = tx.Control
+ TransactionSettings = tx.Settings
+)
+
+// BeginTx returns selector transaction control option
+func BeginTx(opts ...tx.Option) tx.ControlOption {
+ return tx.BeginTx(opts...)
+}
+
+func WithTx(t tx.Identifier) tx.ControlOption {
+ return tx.WithTx(t)
+}
+
+func WithTxID(txID string) tx.ControlOption {
+ return tx.WithTxID(txID)
+}
+
+// CommitTx returns commit transaction control option
+func CommitTx() tx.ControlOption {
+ return tx.CommitTx()
+}
+
+// TxControl makes transaction control from given options
+func TxControl(opts ...tx.ControlOption) *TransactionControl {
+ return tx.NewControl(opts...)
+}
+
+func NoTx() *TransactionControl {
+ return nil
+}
+
+// DefaultTxControl returns default transaction control with serializable read-write isolation mode and auto-commit
+func DefaultTxControl() *TransactionControl {
+ return TxControl(
+ BeginTx(WithSerializableReadWrite()),
+ CommitTx(),
+ )
+}
+
+// SerializableReadWriteTxControl returns transaction control with serializable read-write isolation mode
+func SerializableReadWriteTxControl(opts ...tx.ControlOption) *TransactionControl {
+ return tx.SerializableReadWriteTxControl(opts...)
+}
+
+// OnlineReadOnlyTxControl returns online read-only transaction control
+func OnlineReadOnlyTxControl(opts ...tx.OnlineReadOnlyOption) *TransactionControl {
+ return TxControl(
+ BeginTx(WithOnlineReadOnly(opts...)),
+ CommitTx(), // open transactions not supported for OnlineReadOnly
+ )
+}
+
+// StaleReadOnlyTxControl returns stale read-only transaction control
+func StaleReadOnlyTxControl() *TransactionControl {
+ return TxControl(
+ BeginTx(WithStaleReadOnly()),
+ CommitTx(), // open transactions not supported for StaleReadOnly
+ )
+}
+
+// SnapshotReadOnlyTxControl returns snapshot read-only transaction control
+func SnapshotReadOnlyTxControl() *TransactionControl {
+ return TxControl(
+ BeginTx(WithSnapshotReadOnly()),
+ CommitTx(), // open transactions not supported for StaleReadOnly
+ )
+}
+
+// TxSettings returns transaction settings
+func TxSettings(opts ...tx.Option) TransactionSettings {
+ return opts
+}
+
+func WithDefaultTxMode() tx.Option {
+ return tx.WithDefaultTxMode()
+}
+
+func WithSerializableReadWrite() tx.Option {
+ return tx.WithSerializableReadWrite()
+}
+
+func WithSnapshotReadOnly() tx.Option {
+ return tx.WithSnapshotReadOnly()
+}
+
+func WithStaleReadOnly() tx.Option {
+ return tx.WithStaleReadOnly()
+}
+
+func WithInconsistentReads() tx.OnlineReadOnlyOption {
+ return tx.WithInconsistentReads()
+}
+
+func WithOnlineReadOnly(opts ...tx.OnlineReadOnlyOption) tx.Option {
+ return tx.WithOnlineReadOnly(opts...)
+}
diff --git a/query_bind_test.go b/query_bind_test.go
index 24870c0c9..3ffa07c80 100644
--- a/query_bind_test.go
+++ b/query_bind_test.go
@@ -9,6 +9,7 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/bind"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
"github.com/ydb-platform/ydb-go-sdk/v3/testutil"
@@ -567,14 +568,14 @@ SELECT $param1, $param2`,
},
} {
t.Run("", func(t *testing.T) {
- yql, params, err := tt.b.RewriteQuery(tt.sql, tt.args...)
+ yql, parameters, err := tt.b.RewriteQuery(tt.sql, tt.args...)
if tt.err != nil {
require.Error(t, err)
require.ErrorIs(t, err, tt.err)
} else {
require.NoError(t, err)
require.Equal(t, tt.yql, yql)
- require.Equal(t, tt.params, params)
+ require.Equal(t, []*params.Parameter(*tt.params), parameters)
}
})
}
diff --git a/retry/errors_data_test.go b/retry/errors_data_test.go
index a2ffafa91..5253b505f 100644
--- a/retry/errors_data_test.go
+++ b/retry/errors_data_test.go
@@ -208,7 +208,7 @@ var errsToCheck = []struct {
err: xerrors.Retryable(
xerrors.Transport(grpcStatus.Error(grpcCodes.Unavailable, "")),
xerrors.WithBackoff(backoff.TypeFast),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
),
backoff: backoff.TypeFast,
deleteSession: true,
@@ -221,7 +221,7 @@ var errsToCheck = []struct {
err: xerrors.Retryable(
grpcStatus.Error(grpcCodes.Unavailable, ""),
xerrors.WithBackoff(backoff.TypeFast),
- xerrors.WithDeleteSession(),
+ xerrors.InvalidObject(),
),
backoff: backoff.TypeFast,
deleteSession: true,
diff --git a/retry/mode.go b/retry/mode.go
index 385cf170b..32736e9c3 100644
--- a/retry/mode.go
+++ b/retry/mode.go
@@ -7,10 +7,10 @@ import (
// retryMode reports whether operation is able retried and with which properties.
type retryMode struct {
- code int64
- errType xerrors.Type
- backoff backoff.Type
- deleteSession bool
+ code int64
+ errType xerrors.Type
+ backoff backoff.Type
+ isRetryObjectValid bool
}
func (m retryMode) MustRetry(isOperationIdempotent bool) bool {
@@ -33,4 +33,6 @@ func (m retryMode) MustBackoff() bool { return m.backoff&backoff.TypeAny != 0 }
func (m retryMode) BackoffType() backoff.Type { return m.backoff }
-func (m retryMode) MustDeleteSession() bool { return m.deleteSession }
+func (m retryMode) MustDeleteSession() bool { return !m.isRetryObjectValid }
+
+func (m retryMode) IsRetryObjectValid() bool { return m.isRetryObjectValid }
diff --git a/retry/retry.go b/retry/retry.go
index 5a3617e58..1c97f877f 100644
--- a/retry/retry.go
+++ b/retry/retry.go
@@ -232,7 +232,7 @@ func WithPanicCallback(panicCallback func(e interface{})) panicCallbackOption {
// If you need to retry your op func on some logic errors - you must return RetryableError() from retryOperation
func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr error) {
options := &retryOptions{
- call: stack.FunctionID(""),
+ call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.Retry"),
trace: &trace.Retry{},
fastBackoff: backoff.Fast,
slowBackoff: backoff.Slow,
@@ -258,13 +258,13 @@ func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr err
i int
attempts int
- code = int64(0)
- onIntermediate = trace.RetryOnRetry(options.trace, &ctx,
- options.label, options.call, options.label, options.idempotent, xcontext.IsNestedCall(ctx),
+ code = int64(0)
+ onDone = trace.RetryOnRetry(options.trace, &ctx,
+ options.call, options.label, options.idempotent, xcontext.IsNestedCall(ctx),
)
)
defer func() {
- onIntermediate(finalErr)(attempts, finalErr)
+ onDone(attempts, finalErr)
}()
for {
i++
@@ -331,8 +331,6 @@ func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr err
}
code = m.StatusCode()
-
- onIntermediate(err)
}
}
}
@@ -342,9 +340,9 @@ func Check(err error) (m retryMode) {
code, errType, backoffType, deleteSession := xerrors.Check(err)
return retryMode{
- code: code,
- errType: errType,
- backoff: backoffType,
- deleteSession: deleteSession,
+ code: code,
+ errType: errType,
+ backoff: backoffType,
+ isRetryObjectValid: deleteSession,
}
}
diff --git a/retry/retry_test.go b/retry/retry_test.go
index 728c85e4e..770f2ea65 100644
--- a/retry/retry_test.go
+++ b/retry/retry_test.go
@@ -45,10 +45,10 @@ func TestRetryModes(t *testing.T) {
tt.backoff,
)
}
- if m.MustDeleteSession() != tt.deleteSession {
+ if m.IsRetryObjectValid() != tt.deleteSession {
t.Errorf(
"unexpected delete session status: %v, want: %v",
- m.MustDeleteSession(),
+ m.IsRetryObjectValid(),
tt.deleteSession,
)
}
@@ -148,18 +148,20 @@ func TestRetryTransportDeadlineExceeded(t *testing.T) {
grpcCodes.DeadlineExceeded,
grpcCodes.Canceled,
} {
- counter := 0
- ctx, cancel := xcontext.WithTimeout(context.Background(), time.Hour)
- err := Retry(ctx, func(ctx context.Context) error {
- counter++
- if !(counter < cancelCounterValue) {
- cancel()
- }
+ t.Run(code.String(), func(t *testing.T) {
+ counter := 0
+ ctx, cancel := xcontext.WithTimeout(context.Background(), time.Hour)
+ err := Retry(ctx, func(ctx context.Context) error {
+ counter++
+ if !(counter < cancelCounterValue) {
+ cancel()
+ }
- return xerrors.Transport(grpcStatus.Error(code, ""))
- }, WithIdempotent(true))
- require.ErrorIs(t, err, context.Canceled)
- require.Equal(t, cancelCounterValue, counter)
+ return xerrors.Transport(grpcStatus.Error(code, ""))
+ }, WithIdempotent(true))
+ require.ErrorIs(t, err, context.Canceled)
+ require.Equal(t, cancelCounterValue, counter)
+ })
}
}
@@ -169,17 +171,19 @@ func TestRetryTransportCancelled(t *testing.T) {
grpcCodes.DeadlineExceeded,
grpcCodes.Canceled,
} {
- counter := 0
- ctx, cancel := xcontext.WithCancel(context.Background())
- err := Retry(ctx, func(ctx context.Context) error {
- counter++
- if !(counter < cancelCounterValue) {
- cancel()
- }
+ t.Run(code.String(), func(t *testing.T) {
+ counter := 0
+ ctx, cancel := xcontext.WithCancel(context.Background())
+ err := Retry(ctx, func(ctx context.Context) error {
+ counter++
+ if !(counter < cancelCounterValue) {
+ cancel()
+ }
- return xerrors.Transport(grpcStatus.Error(code, ""))
- }, WithIdempotent(true))
- require.ErrorIs(t, err, context.Canceled)
- require.Equal(t, cancelCounterValue, counter)
+ return xerrors.Transport(grpcStatus.Error(code, ""))
+ }, WithIdempotent(true))
+ require.ErrorIs(t, err, context.Canceled)
+ require.Equal(t, cancelCounterValue, counter)
+ })
}
}
diff --git a/retry/retryable_error.go b/retry/retryable_error.go
index e0778c1df..ca273aac7 100644
--- a/retry/retryable_error.go
+++ b/retry/retryable_error.go
@@ -20,7 +20,7 @@ func WithBackoff(t backoff.Type) retryableErrorOption {
// WithDeleteSession makes retryable error option with delete session flag
func WithDeleteSession() retryableErrorOption {
- return retryableErrorOption(xerrors.WithDeleteSession())
+ return retryableErrorOption(xerrors.InvalidObject())
}
// RetryableError makes retryable error from options
@@ -29,9 +29,9 @@ func RetryableError(err error, opts ...retryableErrorOption) error {
return xerrors.Retryable(
err,
func() (retryableErrorOptions []xerrors.RetryableErrorOption) {
- for _, o := range opts {
- if o != nil {
- retryableErrorOptions = append(retryableErrorOptions, xerrors.RetryableErrorOption(o))
+ for _, opt := range opts {
+ if opt != nil {
+ retryableErrorOptions = append(retryableErrorOptions, xerrors.RetryableErrorOption(opt))
}
}
diff --git a/retry/sql.go b/retry/sql.go
index 115b685ad..affde98e2 100644
--- a/retry/sql.go
+++ b/retry/sql.go
@@ -42,7 +42,7 @@ func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Co
var (
options = doOptions{
retryOptions: []Option{
- withCaller(stack.FunctionID("")),
+ withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.Do")),
},
}
attempts = 0
@@ -129,7 +129,7 @@ func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) err
var (
options = doTxOptions{
retryOptions: []Option{
- withCaller(stack.FunctionID("")),
+ withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.DoTx")),
},
txOptions: &sql.TxOptions{
Isolation: sql.LevelDefault,
diff --git a/retry/sql_test.go b/retry/sql_test.go
index 935179834..d6552493b 100644
--- a/retry/sql_test.go
+++ b/retry/sql_test.go
@@ -11,7 +11,6 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/stack"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql/badconn"
- "github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
type mockConnector struct {
@@ -104,7 +103,7 @@ func (m *mockConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.T
func (m *mockConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
m.t.Log(stack.Record(0))
- if xerrors.MustDeleteSession(m.execErr) {
+ if !xerrors.IsRetryObjectValid(m.execErr) {
m.closed = true
}
@@ -113,7 +112,7 @@ func (m *mockConn) QueryContext(ctx context.Context, query string, args []driver
func (m *mockConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
m.t.Log(stack.Record(0))
- if xerrors.MustDeleteSession(m.execErr) {
+ if !xerrors.IsRetryObjectValid(m.execErr) {
m.closed = true
}
@@ -215,18 +214,6 @@ func TestDoTx(t *testing.T) {
WithIdempotent(bool(idempotentType)),
WithFastBackoff(backoff.New(backoff.WithSlotDuration(time.Nanosecond))),
WithSlowBackoff(backoff.New(backoff.WithSlotDuration(time.Nanosecond))),
- WithTrace(&trace.Retry{
- //nolint:lll
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- t.Logf("attempt %d, conn %d, mode: %+v", attempts, m.conns, Check(m.queryErr))
-
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- t.Logf("attempt %d, conn %d, mode: %+v", attempts, m.conns, Check(m.queryErr))
-
- return nil
- }
- },
- }),
)
if tt.canRetry[idempotentType] {
if err != nil {
diff --git a/scheme/scheme.go b/scheme/scheme.go
index c7a3e9274..1aeac9777 100644
--- a/scheme/scheme.go
+++ b/scheme/scheme.go
@@ -110,11 +110,11 @@ func (e *Entry) IsTopic() bool {
func (e *Entry) From(y *Ydb_Scheme.Entry) {
*e = Entry{
- Name: y.Name,
- Owner: y.Owner,
- Type: entryType(y.Type),
- Permissions: makePermissions(y.Permissions),
- EffectivePermissions: makePermissions(y.EffectivePermissions),
+ Name: y.GetName(),
+ Owner: y.GetOwner(),
+ Type: entryType(y.GetType()),
+ Permissions: makePermissions(y.GetPermissions()),
+ EffectivePermissions: makePermissions(y.GetEffectivePermissions()),
}
}
@@ -155,8 +155,8 @@ func makePermissions(src []*Ydb_Scheme.Permissions) (dst []Permissions) {
func from(y *Ydb_Scheme.Permissions) (p Permissions) {
return Permissions{
- Subject: y.Subject,
- PermissionNames: y.PermissionNames,
+ Subject: y.GetSubject(),
+ PermissionNames: y.GetPermissionNames(),
}
}
diff --git a/scripting/scripting.go b/scripting/scripting.go
index dc31def50..048529fee 100644
--- a/scripting/scripting.go
+++ b/scripting/scripting.go
@@ -3,6 +3,7 @@ package scripting
import (
"context"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
)
@@ -21,7 +22,7 @@ type Client interface {
Execute(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ params *params.Parameters,
) (result.Result, error)
Explain(
ctx context.Context,
@@ -31,6 +32,6 @@ type Client interface {
StreamExecute(
ctx context.Context,
query string,
- params *table.QueryParameters,
+ params *params.Parameters,
) (result.StreamResult, error)
}
diff --git a/sugar/params.go b/sugar/params.go
index 24c7f897c..612268260 100644
--- a/sugar/params.go
+++ b/sugar/params.go
@@ -3,37 +3,32 @@ package sugar
import (
"database/sql"
"fmt"
+ "sort"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/bind"
- internal "github.com/ydb-platform/ydb-go-sdk/v3/internal/table"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
)
+type constraint interface {
+ params.Parameters | []*params.Parameter | *table.QueryParameters | []table.ParameterOption | []sql.NamedArg
+}
+
// GenerateDeclareSection generates DECLARE section text in YQL query by params
//
// Deprecated: use testutil.QueryBind(ydb.WithAutoDeclare()) helper
-func GenerateDeclareSection[T *table.QueryParameters | []table.ParameterOption | []sql.NamedArg](
- params T,
-) (string, error) {
- switch v := any(params).(type) {
- case *table.QueryParameters:
- return internal.GenerateDeclareSection(v)
+func GenerateDeclareSection[T constraint](parameters T) (string, error) {
+ switch v := any(parameters).(type) {
+ case *params.Parameters:
+ return parametersToDeclares(*v), nil
+ case []*params.Parameter:
+ return parametersToDeclares(v), nil
case []table.ParameterOption:
- return internal.GenerateDeclareSection(table.NewQueryParameters(v...))
+ return parameterOptionsToDeclares(v), nil
case []sql.NamedArg:
- values, err := bind.Params(func() (newArgs []interface{}) {
- for i := range v {
- newArgs = append(newArgs, v[i])
- }
-
- return newArgs
- }()...)
- if err != nil {
- return "", xerrors.WithStackTrace(err)
- }
-
- return internal.GenerateDeclareSection(table.NewQueryParameters(values...))
+ return namedArgsToDeclares(v)
default:
return "", xerrors.WithStackTrace(fmt.Errorf("unsupported type: %T", v))
}
@@ -42,7 +37,7 @@ func GenerateDeclareSection[T *table.QueryParameters | []table.ParameterOption |
// ToYdbParam converts
//
// Deprecated: use testutil/QueryBind helper
-func ToYdbParam(param sql.NamedArg) (table.ParameterOption, error) {
+func ToYdbParam(param sql.NamedArg) (*params.Parameter, error) {
params, err := bind.Params(param)
if err != nil {
return nil, xerrors.WithStackTrace(err)
@@ -53,3 +48,51 @@ func ToYdbParam(param sql.NamedArg) (table.ParameterOption, error) {
return params[0], nil
}
+
+func parametersToDeclares(v []*params.Parameter) string {
+ var (
+ buf = xstring.Buffer()
+ names = make([]string, 0, len(v))
+ declares = make(map[string]string, len(v))
+ )
+ defer buf.Free()
+
+ for _, p := range v {
+ name := p.Name()
+ names = append(names, name)
+ declares[name] = params.Declare(p)
+ }
+
+ sort.Strings(names)
+
+ for _, name := range names {
+ buf.WriteString(declares[name])
+ buf.WriteString(";\n")
+ }
+
+ return buf.String()
+}
+
+func parameterOptionsToDeclares(v []table.ParameterOption) string {
+ parameters := make([]*params.Parameter, len(v))
+ for i, p := range v {
+ parameters[i] = params.Named(p.Name(), p.Value())
+ }
+
+ return parametersToDeclares(parameters)
+}
+
+func namedArgsToDeclares(v []sql.NamedArg) (string, error) {
+ vv, err := bind.Params(func() (newArgs []interface{}) {
+ for i := range v {
+ newArgs = append(newArgs, v[i])
+ }
+
+ return newArgs
+ }()...)
+ if err != nil {
+ return "", xerrors.WithStackTrace(err)
+ }
+
+ return parametersToDeclares(vv), nil
+}
diff --git a/sugar/result.go b/sugar/result.go
new file mode 100644
index 000000000..33c2339d9
--- /dev/null
+++ b/sugar/result.go
@@ -0,0 +1,83 @@
+package sugar
+
+import (
+ "context"
+ "errors"
+ "io"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/query/scanner"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
+ "github.com/ydb-platform/ydb-go-sdk/v3/table/result/named"
+)
+
+type result struct {
+ r query.Result
+ rs query.ResultSet
+ row query.Row
+}
+
+func (r *result) NextResultSet(ctx context.Context) bool {
+ var err error
+ r.rs, err = r.r.NextResultSet(ctx)
+ if err != nil && errors.Is(err, io.EOF) {
+ return false
+ }
+
+ return err == nil && r.rs != nil && r.r.Err() == nil
+}
+
+func (r *result) NextRow() bool {
+ if r.rs == nil {
+ return false
+ }
+
+ var err error
+ r.row, err = r.rs.NextRow(context.Background())
+ if err != nil && errors.Is(err, io.EOF) {
+ return false
+ }
+
+ return r.row != nil && r.r.Err() == nil
+}
+
+func (r *result) Scan(indexedValues ...indexed.RequiredOrOptional) error {
+ values := make([]interface{}, 0, len(indexedValues))
+ for _, value := range indexedValues {
+ values = append(values, value)
+ }
+
+ return r.row.Scan(values...)
+}
+
+func (r *result) ScanNamed(namedValues ...named.Value) error {
+ values := make([]scanner.NamedDestination, 0, len(namedValues))
+ for i := range namedValues {
+ values = append(values, scanner.NamedRef(namedValues[i].Name, namedValues[i].Value))
+ }
+
+ return r.row.ScanNamed(values...)
+}
+
+func (r *result) ScanStruct(dst interface{}) error {
+ return r.row.ScanStruct(dst)
+}
+
+func (r *result) Err() error {
+ return r.r.Err()
+}
+
+func (r *result) Close() error {
+ return r.r.Close(context.Background())
+}
+
+// Result converts query.Result to iterable result for compatibility with table/result.Result usage
+//
+// # Experimental
+//
+// Notice: This API is EXPERIMENTAL and may be changed or removed in a later release.
+func Result(r query.Result) *result {
+ return &result{
+ r: r,
+ }
+}
diff --git a/table/options/models.go b/table/options/models.go
index 1464defe4..4692d9d4a 100644
--- a/table/options/models.go
+++ b/table/options/models.go
@@ -9,8 +9,8 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/feature"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
type Column struct {
@@ -22,7 +22,7 @@ type Column struct {
func (c Column) toYDB(a *allocator.Allocator) *Ydb_Table.ColumnMeta {
return &Ydb_Table.ColumnMeta{
Name: c.Name,
- Type: value.TypeToYDB(c.Type, a),
+ Type: types.TypeToYDB(c.Type, a),
Family: c.Family,
}
}
@@ -43,15 +43,6 @@ type IndexDescription struct {
Type IndexType
}
-//nolint:unused
-func (i IndexDescription) toYDB() *Ydb_Table.TableIndexDescription {
- return &Ydb_Table.TableIndexDescription{
- Name: i.Name,
- IndexColumns: i.IndexColumns,
- Status: i.Status,
- }
-}
-
type Description struct {
Name string
Columns []Column
@@ -406,8 +397,8 @@ type (
)
type KeyRange struct {
- From types.Value
- To types.Value
+ From value.Value
+ To value.Value
}
func (kr KeyRange) String() string {
diff --git a/table/options/options.go b/table/options/options.go
index df33cf0c8..db85556a7 100644
--- a/table/options/options.go
+++ b/table/options/options.go
@@ -6,8 +6,8 @@ import (
"google.golang.org/grpc"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
func WithShardKeyBounds() DescribeTableOption {
@@ -59,14 +59,14 @@ type column struct {
func (c column) ApplyAlterTableOption(d *AlterTableDesc, a *allocator.Allocator) {
d.AddColumns = append(d.AddColumns, &Ydb_Table.ColumnMeta{
Name: c.name,
- Type: value.TypeToYDB(c.typ, a),
+ Type: types.TypeToYDB(c.typ, a),
})
}
func (c column) ApplyCreateTableOption(d *CreateTableDesc, a *allocator.Allocator) {
d.Columns = append(d.Columns, &Ydb_Table.ColumnMeta{
Name: c.name,
- Type: value.TypeToYDB(c.typ, a),
+ Type: types.TypeToYDB(c.typ, a),
})
}
@@ -161,7 +161,9 @@ func (i index) ApplyAlterTableOption(d *AlterTableDesc, a *allocator.Allocator)
Name: i.name,
}
for _, opt := range i.opts {
- opt.ApplyIndexOption((*indexDesc)(x))
+ if opt != nil {
+ opt.ApplyIndexOption((*indexDesc)(x))
+ }
}
d.AddIndexes = append(d.AddIndexes, x)
}
@@ -171,7 +173,9 @@ func (i index) ApplyCreateTableOption(d *CreateTableDesc, a *allocator.Allocator
Name: i.name,
}
for _, opt := range i.opts {
- opt.ApplyIndexOption((*indexDesc)(x))
+ if opt != nil {
+ opt.ApplyIndexOption((*indexDesc)(x))
+ }
}
d.Indexes = append(d.Indexes, x)
}
@@ -304,7 +308,7 @@ func WithUniformPartitions(n uint64) Partitions {
return uniformPartitions(n)
}
-type explicitPartitions []types.Value
+type explicitPartitions []value.Value
func (e explicitPartitions) ApplyCreateTableOption(d *CreateTableDesc, a *allocator.Allocator) {
values := make([]*Ydb.TypedValue, len(e))
@@ -320,7 +324,7 @@ func (e explicitPartitions) ApplyCreateTableOption(d *CreateTableDesc, a *alloca
func (e explicitPartitions) isPartitions() {}
-func WithExplicitPartitions(splitPoints ...types.Value) Partitions {
+func WithExplicitPartitions(splitPoints ...value.Value) Partitions {
return explicitPartitions(splitPoints)
}
@@ -524,7 +528,7 @@ func WithPartitioningPolicyUniformPartitions(n uint64) PartitioningPolicyOption
}
// Deprecated: use WithExplicitPartitions instead
-func WithPartitioningPolicyExplicitPartitions(splitPoints ...types.Value) PartitioningPolicyOption {
+func WithPartitioningPolicyExplicitPartitions(splitPoints ...value.Value) PartitioningPolicyOption {
return func(p *partitioningPolicy, a *allocator.Allocator) {
values := make([]*Ydb.TypedValue, len(splitPoints))
for i := range values {
@@ -1036,10 +1040,10 @@ type (
readOrderedOption struct{}
readSnapshotOption bool
readKeyRangeOption KeyRange
- readGreaterOrEqualOption struct{ types.Value }
- readLessOrEqualOption struct{ types.Value }
- readLessOption struct{ types.Value }
- readGreaterOption struct{ types.Value }
+ readGreaterOrEqualOption struct{ value.Value }
+ readLessOrEqualOption struct{ value.Value }
+ readLessOption struct{ value.Value }
+ readGreaterOption struct{ value.Value }
readRowLimitOption uint64
)
@@ -1133,19 +1137,19 @@ func ReadKeyRange(x KeyRange) ReadTableOption {
return readKeyRangeOption(x)
}
-func ReadGreater(x types.Value) ReadTableOption {
+func ReadGreater(x value.Value) ReadTableOption {
return readGreaterOption{x}
}
-func ReadGreaterOrEqual(x types.Value) ReadTableOption {
+func ReadGreaterOrEqual(x value.Value) ReadTableOption {
return readGreaterOrEqualOption{x}
}
-func ReadLess(x types.Value) ReadTableOption {
+func ReadLess(x value.Value) ReadTableOption {
return readLessOption{x}
}
-func ReadLessOrEqual(x types.Value) ReadTableOption {
+func ReadLessOrEqual(x value.Value) ReadTableOption {
return readLessOrEqualOption{x}
}
diff --git a/table/options/options_test.go b/table/options/options_test.go
index d702a8fb0..3b639ce6a 100644
--- a/table/options/options_test.go
+++ b/table/options/options_test.go
@@ -9,8 +9,8 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/feature"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
var abc = "abc"
@@ -24,7 +24,7 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- if req.Profile.PresetName != abc {
+ if req.GetProfile().GetPresetName() != abc {
t.Errorf("Preset is not as expected")
}
}
@@ -34,7 +34,7 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- if req.Profile.CompactionPolicy.PresetName != abc {
+ if req.GetProfile().GetCompactionPolicy().GetPresetName() != abc {
t.Errorf("Compaction policy is not as expected")
}
}
@@ -47,8 +47,8 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- p := req.Profile.PartitioningPolicy
- if p.PresetName != abc || p.AutoPartitioning != Ydb_Table.PartitioningPolicy_AUTO_SPLIT {
+ p := req.GetProfile().GetPartitioningPolicy()
+ if p.GetPresetName() != abc || p.GetAutoPartitioning() != Ydb_Table.PartitioningPolicy_AUTO_SPLIT {
t.Errorf("Partitioning policy is not as expected")
}
}
@@ -58,26 +58,26 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- if p, ok := req.Partitions.(*Ydb_Table.CreateTableRequest_UniformPartitions); !ok || p.UniformPartitions != 3 {
+ if p, ok := req.GetPartitions().(*Ydb_Table.CreateTableRequest_UniformPartitions); !ok || p.UniformPartitions != 3 {
t.Errorf("Uniform partitioning policy is not as expected")
}
}
{
opt := WithPartitions(
WithExplicitPartitions(
- types.Int64Value(1),
+ value.Int64Value(1),
),
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- p, ok := req.Partitions.(*Ydb_Table.CreateTableRequest_PartitionAtKeys)
+ p, ok := req.GetPartitions().(*Ydb_Table.CreateTableRequest_PartitionAtKeys)
if !ok {
t.Errorf("Explicitly partitioning policy is not as expected")
} else {
require.Equal(
t,
- []*Ydb.TypedValue{value.ToYDB(types.Int64Value(1), a)},
- p.PartitionAtKeys.SplitPoints,
+ []*Ydb.TypedValue{value.ToYDB(value.Int64Value(1), a)},
+ p.PartitionAtKeys.GetSplitPoints(),
)
}
}
@@ -87,7 +87,7 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- if req.Profile.ExecutionPolicy.PresetName != abc {
+ if req.GetProfile().GetExecutionPolicy().GetPresetName() != abc {
t.Errorf("Execution policy is not as expected")
}
}
@@ -102,11 +102,11 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- p := req.Profile.ReplicationPolicy
- if p.PresetName != abc ||
- p.ReplicasCount != 3 ||
- p.CreatePerAvailabilityZone != Ydb.FeatureFlag_ENABLED ||
- p.AllowPromotion != Ydb.FeatureFlag_DISABLED {
+ p := req.GetProfile().GetReplicationPolicy()
+ if p.GetPresetName() != abc ||
+ p.GetReplicasCount() != 3 ||
+ p.GetCreatePerAvailabilityZone() != Ydb.FeatureFlag_ENABLED ||
+ p.GetAllowPromotion() != Ydb.FeatureFlag_DISABLED {
t.Errorf("Replication policy is not as expected")
}
}
@@ -116,7 +116,7 @@ func TestSessionOptionsProfile(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- if req.Profile.CachingPolicy.PresetName != abc {
+ if req.GetProfile().GetCachingPolicy().GetPresetName() != abc {
t.Errorf("Caching policy is not as expected")
}
}
@@ -138,13 +138,13 @@ func TestStoragePolicyOptions(t *testing.T) {
)
req := Ydb_Table.CreateTableRequest{}
opt.ApplyCreateTableOption((*CreateTableDesc)(&req), a)
- p := req.Profile.StoragePolicy
- if p.PresetName != abc ||
- p.Syslog.Media != "any1" ||
- p.Log.Media != "any2" ||
- p.Data.Media != "any3" ||
- p.External.Media != "any4" ||
- p.KeepInMemory != Ydb.FeatureFlag_ENABLED {
+ p := req.GetProfile().GetStoragePolicy()
+ if p.GetPresetName() != abc ||
+ p.GetSyslog().GetMedia() != "any1" ||
+ p.GetLog().GetMedia() != "any2" ||
+ p.GetData().GetMedia() != "any3" ||
+ p.GetExternal().GetMedia() != "any4" ||
+ p.GetKeepInMemory() != Ydb.FeatureFlag_ENABLED {
t.Errorf("Storage policy is not as expected")
}
}
@@ -154,27 +154,27 @@ func TestAlterTableOptions(t *testing.T) {
a := allocator.New()
defer a.Free()
{
- opt := WithAddColumn("a", types.TypeBool)
+ opt := WithAddColumn("a", types.Bool)
req := Ydb_Table.AlterTableRequest{}
opt.ApplyAlterTableOption((*AlterTableDesc)(&req), a)
- if len(req.AddColumns) != 1 ||
- req.AddColumns[0].Name != "a" {
+ if len(req.GetAddColumns()) != 1 ||
+ req.GetAddColumns()[0].GetName() != "a" {
t.Errorf("Alter table options is not as expected")
}
}
{
column := Column{
Name: "a",
- Type: types.TypeBool,
+ Type: types.Bool,
Family: "b",
}
opt := WithAddColumnMeta(column)
req := Ydb_Table.AlterTableRequest{}
opt.ApplyAlterTableOption((*AlterTableDesc)(&req), a)
- if len(req.AddColumns) != 1 ||
- req.AddColumns[0].Name != column.Name ||
- req.AddColumns[0].Type != value.TypeToYDB(column.Type, a) ||
- req.AddColumns[0].Family != column.Family {
+ if len(req.GetAddColumns()) != 1 ||
+ req.GetAddColumns()[0].GetName() != column.Name ||
+ req.GetAddColumns()[0].GetType() != types.TypeToYDB(column.Type, a) ||
+ req.GetAddColumns()[0].GetFamily() != column.Family {
t.Errorf("Alter table options is not as expected")
}
}
@@ -182,8 +182,8 @@ func TestAlterTableOptions(t *testing.T) {
opt := WithDropColumn("a")
req := Ydb_Table.AlterTableRequest{}
opt.ApplyAlterTableOption((*AlterTableDesc)(&req), a)
- if len(req.DropColumns) != 1 ||
- req.DropColumns[0] != "a" {
+ if len(req.GetDropColumns()) != 1 ||
+ req.GetDropColumns()[0] != "a" {
t.Errorf("Alter table options is not as expected")
}
}
@@ -199,11 +199,11 @@ func TestAlterTableOptions(t *testing.T) {
opt := WithAlterColumnFamilies(cf)
req := Ydb_Table.AlterTableRequest{}
opt.ApplyAlterTableOption((*AlterTableDesc)(&req), a)
- if len(req.AddColumnFamilies) != 1 ||
- req.AddColumnFamilies[0].Name != cf.Name ||
- req.AddColumnFamilies[0].Data.Media != cf.Data.Media ||
- req.AddColumnFamilies[0].Compression != cf.Compression.toYDB() ||
- req.AddColumnFamilies[0].KeepInMemory != cf.KeepInMemory.ToYDB() {
+ if len(req.GetAddColumnFamilies()) != 1 ||
+ req.GetAddColumnFamilies()[0].GetName() != cf.Name ||
+ req.GetAddColumnFamilies()[0].GetData().GetMedia() != cf.Data.Media ||
+ req.GetAddColumnFamilies()[0].GetCompression() != cf.Compression.toYDB() ||
+ req.GetAddColumnFamilies()[0].GetKeepInMemory() != cf.KeepInMemory.ToYDB() {
t.Errorf("Alter table options is not as expected")
}
}
@@ -215,11 +215,11 @@ func TestAlterTableOptions(t *testing.T) {
opt := WithAlterColumnFamilies(cf)
req := Ydb_Table.AlterTableRequest{}
opt.ApplyAlterTableOption((*AlterTableDesc)(&req), a)
- if len(req.AddColumnFamilies) != 1 ||
- req.AddColumnFamilies[0].Name != cf.Name ||
- req.AddColumnFamilies[0].Data != nil ||
- req.AddColumnFamilies[0].Compression != cf.Compression.toYDB() ||
- req.AddColumnFamilies[0].KeepInMemory != Ydb.FeatureFlag_STATUS_UNSPECIFIED {
+ if len(req.GetAddColumnFamilies()) != 1 ||
+ req.GetAddColumnFamilies()[0].GetName() != cf.Name ||
+ req.GetAddColumnFamilies()[0].GetData() != nil ||
+ req.GetAddColumnFamilies()[0].GetCompression() != cf.Compression.toYDB() ||
+ req.GetAddColumnFamilies()[0].GetKeepInMemory() != Ydb.FeatureFlag_STATUS_UNSPECIFIED {
t.Errorf("Alter table options is not as expected")
}
}
diff --git a/table/result/indexed/indexed.go b/table/result/indexed/indexed.go
index f92b205c5..af8a637d6 100644
--- a/table/result/indexed/indexed.go
+++ b/table/result/indexed/indexed.go
@@ -27,7 +27,7 @@ type Optional interface{}
// RequiredOrOptional is a type scan destination of ydb values
// This is a proxy type for preparing go1.18 type set constrains such as
//
-// type Value interface {
+// type valueType interface {
// Required | Optional
// }
type RequiredOrOptional interface{}
diff --git a/table/result/result.go b/table/result/result.go
index c8514187d..e82860f3d 100644
--- a/table/result/result.go
+++ b/table/result/result.go
@@ -91,7 +91,7 @@ type BaseResult interface {
// string
// time.Time
// time.Duration
- // ydb.Value
+ // ydb.valueType
// For custom types implement sql.Scanner or json.Unmarshaler interface.
// For optional types use double pointer construction.
// For unknown types use interface types.
diff --git a/table/table.go b/table/table.go
index d2d4b1001..6d2305a1e 100644
--- a/table/table.go
+++ b/table/table.go
@@ -2,20 +2,17 @@ package table
import (
"context"
- "sort"
"time"
- "github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Table"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/closer"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/params"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring"
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
@@ -144,7 +141,7 @@ type Session interface {
ctx context.Context,
tx *TransactionControl,
query string,
- params *QueryParameters,
+ params *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (txr Transaction, r result.Result, err error)
@@ -167,21 +164,21 @@ type Session interface {
StreamExecuteScanQuery(
ctx context.Context,
query string,
- params *QueryParameters,
+ params *params.Parameters,
opts ...options.ExecuteScanQueryOption,
) (_ result.StreamResult, err error)
BulkUpsert(
ctx context.Context,
table string,
- rows types.Value,
+ rows value.Value,
opts ...options.BulkUpsertOption,
) (err error)
ReadRows(
ctx context.Context,
path string,
- keys types.Value,
+ keys value.Value,
opts ...options.ReadRowsOption,
) (_ result.Result, err error)
@@ -243,13 +240,13 @@ type TransactionActor interface {
Execute(
ctx context.Context,
query string,
- params *QueryParameters,
+ params *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (result.Result, error)
ExecuteStatement(
ctx context.Context,
stmt Statement,
- params *QueryParameters,
+ params *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (result.Result, error)
}
@@ -270,7 +267,7 @@ type Statement interface {
Execute(
ctx context.Context,
tx *TransactionControl,
- params *QueryParameters,
+ params *params.Parameters,
opts ...options.ExecuteDataQueryOption,
) (txr Transaction, r result.Result, err error)
NumInput() int
@@ -455,113 +452,22 @@ func SnapshotReadOnlyTxControl() *TransactionControl {
// QueryParameters
type (
- queryParams map[string]types.Value
- ParameterOption interface {
- Name() string
- Value() types.Value
- }
- parameterOption struct {
- name string
- value types.Value
- }
- QueryParameters struct {
- m queryParams
- }
+ ParameterOption = params.NamedValue
+ QueryParameters = params.Parameters
)
-func (p parameterOption) Name() string {
- return p.name
-}
-
-func (p parameterOption) Value() types.Value {
- return p.value
-}
-
-func (qp queryParams) ToYDB(a *allocator.Allocator) map[string]*Ydb.TypedValue {
- if qp == nil {
- return nil
- }
- params := make(map[string]*Ydb.TypedValue, len(qp))
- for k, v := range qp {
- params[k] = value.ToYDB(v, a)
- }
-
- return params
-}
-
-func (q *QueryParameters) Params() queryParams {
- if q == nil {
- return nil
- }
-
- return q.m
-}
-
-func (q *QueryParameters) Count() int {
- if q == nil {
- return 0
- }
-
- return len(q.m)
-}
-
-func (q *QueryParameters) Each(it func(name string, v types.Value)) {
- if q == nil {
- return
- }
- for key, v := range q.m {
- it(key, v)
- }
-}
-
-func (q *QueryParameters) names() []string {
- if q == nil {
- return nil
- }
- names := make([]string, 0, len(q.m))
- for k := range q.m {
- names = append(names, k)
- }
- sort.Strings(names)
-
- return names
-}
-
-func (q *QueryParameters) String() string {
- buffer := xstring.Buffer()
- defer buffer.Free()
-
- buffer.WriteByte('{')
- for i, name := range q.names() {
- if i != 0 {
- buffer.WriteByte(',')
- }
- buffer.WriteByte('"')
- buffer.WriteString(name)
- buffer.WriteString("\":")
- buffer.WriteString(q.m[name].Yql())
- }
- buffer.WriteByte('}')
-
- return buffer.String()
-}
-
func NewQueryParameters(opts ...ParameterOption) *QueryParameters {
- q := &QueryParameters{
- m: make(queryParams, len(opts)),
+ qp := QueryParameters(make([]*params.Parameter, len(opts)))
+ for i, opt := range opts {
+ if opt != nil {
+ qp[i] = params.Named(opt.Name(), opt.Value())
+ }
}
- q.Add(opts...)
- return q
+ return &qp
}
-func (q *QueryParameters) Add(params ...ParameterOption) {
- for _, param := range params {
- q.m[param.Name()] = param.Value()
- }
-}
-
-func ValueParam(name string, v types.Value) ParameterOption {
+func ValueParam(name string, v value.Value) ParameterOption {
switch len(name) {
case 0:
panic("empty name")
@@ -571,10 +477,7 @@ func ValueParam(name string, v types.Value) ParameterOption {
}
}
- return ¶meterOption{
- name: name,
- value: v,
- }
+ return params.Named(name, v)
}
type Options struct {
diff --git a/table/types/types.go b/table/types/types.go
index f492e7b30..f6184943f 100644
--- a/table/types/types.go
+++ b/table/types/types.go
@@ -2,10 +2,9 @@ package types
import (
"bytes"
- "io"
- "time"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/scanner"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
)
const (
@@ -14,30 +13,30 @@ const (
)
// Type describes YDB data types.
-type Type = value.Type
+type Type = types.Type
// Equal checks for type equivalence
func Equal(lhs, rhs Type) bool {
- return value.TypesEqual(lhs, rhs)
+ return types.Equal(lhs, rhs)
}
func List(t Type) Type {
- return value.List(t)
+ return types.NewList(t)
}
func Tuple(elems ...Type) Type {
- return value.Tuple(elems...)
+ return types.NewTuple(elems...)
}
type tStructType struct {
- fields []value.StructField
+ fields []types.StructField
}
type StructOption func(*tStructType)
func StructField(name string, t Type) StructOption {
return func(s *tStructType) {
- s.fields = append(s.fields, value.StructField{
+ s.fields = append(s.fields, types.StructField{
Name: name,
T: t,
})
@@ -52,11 +51,11 @@ func Struct(opts ...StructOption) Type {
}
}
- return value.Struct(s.fields...)
+ return types.NewStruct(s.fields...)
}
func Dict(k, v Type) Type {
- return value.Dict(k, v)
+ return types.NewDict(k, v)
}
func VariantStruct(opts ...StructOption) Type {
@@ -67,61 +66,61 @@ func VariantStruct(opts ...StructOption) Type {
}
}
- return value.VariantStruct(s.fields...)
+ return types.NewVariantStruct(s.fields...)
}
func VariantTuple(elems ...Type) Type {
- return value.VariantTuple(elems...)
+ return types.NewVariantTuple(elems...)
}
func Void() Type {
- return value.Void()
+ return types.NewVoid()
}
func Optional(t Type) Type {
- return value.Optional(t)
+ return types.NewOptional(t)
}
var DefaultDecimal = DecimalType(decimalPrecision, decimalScale)
func DecimalType(precision, scale uint32) Type {
- return value.Decimal(precision, scale)
+ return types.NewDecimal(precision, scale)
}
func DecimalTypeFromDecimal(d *Decimal) Type {
- return value.Decimal(d.Precision, d.Scale)
+ return types.NewDecimal(d.Precision, d.Scale)
}
// Primitive types known by YDB.
const (
- TypeUnknown = value.TypeUnknown
- TypeBool = value.TypeBool
- TypeInt8 = value.TypeInt8
- TypeUint8 = value.TypeUint8
- TypeInt16 = value.TypeInt16
- TypeUint16 = value.TypeUint16
- TypeInt32 = value.TypeInt32
- TypeUint32 = value.TypeUint32
- TypeInt64 = value.TypeInt64
- TypeUint64 = value.TypeUint64
- TypeFloat = value.TypeFloat
- TypeDouble = value.TypeDouble
- TypeDate = value.TypeDate
- TypeDatetime = value.TypeDatetime
- TypeTimestamp = value.TypeTimestamp
- TypeInterval = value.TypeInterval
- TypeTzDate = value.TypeTzDate
- TypeTzDatetime = value.TypeTzDatetime
- TypeTzTimestamp = value.TypeTzTimestamp
- TypeString = value.TypeBytes
- TypeBytes = value.TypeBytes
- TypeUTF8 = value.TypeText
- TypeText = value.TypeText
- TypeYSON = value.TypeYSON
- TypeJSON = value.TypeJSON
- TypeUUID = value.TypeUUID
- TypeJSONDocument = value.TypeJSONDocument
- TypeDyNumber = value.TypeDyNumber
+ TypeUnknown = types.Unknown
+ TypeBool = types.Bool
+ TypeInt8 = types.Int8
+ TypeUint8 = types.Uint8
+ TypeInt16 = types.Int16
+ TypeUint16 = types.Uint16
+ TypeInt32 = types.Int32
+ TypeUint32 = types.Uint32
+ TypeInt64 = types.Int64
+ TypeUint64 = types.Uint64
+ TypeFloat = types.Float
+ TypeDouble = types.Double
+ TypeDate = types.Date
+ TypeDatetime = types.Datetime
+ TypeTimestamp = types.Timestamp
+ TypeInterval = types.Interval
+ TypeTzDate = types.TzDate
+ TypeTzDatetime = types.TzDatetime
+ TypeTzTimestamp = types.TzTimestamp
+ TypeString = types.Bytes
+ TypeBytes = types.Bytes
+ TypeUTF8 = types.Text
+ TypeText = types.Text
+ TypeYSON = types.YSON
+ TypeJSON = types.JSON
+ TypeUUID = types.UUID
+ TypeJSONDocument = types.JSONDocument
+ TypeDyNumber = types.DyNumber
)
// WriteTypeStringTo writes ydb type string representation into buffer
@@ -131,137 +130,7 @@ func WriteTypeStringTo(buf *bytes.Buffer, t Type) { //nolint: interfacer
buf.WriteString(t.Yql())
}
-// RawValue scanning non-primitive yql types or for own implementation scanner native API
-type RawValue interface {
- Path() string
- WritePathTo(w io.Writer) (n int64, err error)
- Type() Type
- Bool() (v bool)
- Int8() (v int8)
- Uint8() (v uint8)
- Int16() (v int16)
- Uint16() (v uint16)
- Int32() (v int32)
- Uint32() (v uint32)
- Int64() (v int64)
- Uint64() (v uint64)
- Float() (v float32)
- Double() (v float64)
- Date() (v time.Time)
- Datetime() (v time.Time)
- Timestamp() (v time.Time)
- Interval() (v time.Duration)
- TzDate() (v time.Time)
- TzDatetime() (v time.Time)
- TzTimestamp() (v time.Time)
- String() (v []byte)
- UTF8() (v string)
- YSON() (v []byte)
- JSON() (v []byte)
- UUID() (v [16]byte)
- JSONDocument() (v []byte)
- DyNumber() (v string)
- Value() Value
-
- // Any returns any primitive or optional value.
- // Currently, it may return one of these types:
- //
- // bool
- // int8
- // uint8
- // int16
- // uint16
- // int32
- // uint32
- // int64
- // uint64
- // float32
- // float64
- // []byte
- // string
- // [16]byte
- //
- Any() interface{}
-
- // Unwrap unwraps current item under scan interpreting it as Optional types.
- Unwrap()
- AssertType(t Type) bool
- IsNull() bool
- IsOptional() bool
-
- // ListIn interprets current item under scan as a ydb's list.
- // It returns the size of the nested items.
- // If current item under scan is not a list types, it returns -1.
- ListIn() (size int)
-
- // ListItem selects current item i-th element as an item to scan.
- // ListIn() must be called before.
- ListItem(i int)
-
- // ListOut leaves list entered before by ListIn() call.
- ListOut()
-
- // TupleIn interprets current item under scan as a ydb's tuple.
- // It returns the size of the nested items.
- TupleIn() (size int)
-
- // TupleItem selects current item i-th element as an item to scan.
- // Note that TupleIn() must be called before.
- // It panics if it is out of bounds.
- TupleItem(i int)
-
- // TupleOut leaves tuple entered before by TupleIn() call.
- TupleOut()
-
- // StructIn interprets current item under scan as a ydb's struct.
- // It returns the size of the nested items – the struct fields values.
- // If there is no current item under scan it returns -1.
- StructIn() (size int)
-
- // StructField selects current item i-th field value as an item to scan.
- // Note that StructIn() must be called before.
- // It panics if i is out of bounds.
- StructField(i int) (name string)
-
- // StructOut leaves struct entered before by StructIn() call.
- StructOut()
-
- // DictIn interprets current item under scan as a ydb's dict.
- // It returns the size of the nested items pairs.
- // If there is no current item under scan it returns -1.
- DictIn() (size int)
-
- // DictKey selects current item i-th pair key as an item to scan.
- // Note that DictIn() must be called before.
- // It panics if i is out of bounds.
- DictKey(i int)
-
- // DictPayload selects current item i-th pair value as an item to scan.
- // Note that DictIn() must be called before.
- // It panics if i is out of bounds.
- DictPayload(i int)
-
- // DictOut leaves dict entered before by DictIn() call.
- DictOut()
-
- // Variant unwraps current item under scan interpreting it as Variant types.
- // It returns non-empty name of a field that is filled for struct-based
- // variant.
- // It always returns an index of filled field of a Type.
- Variant() (name string, index uint32)
-
- // Decimal returns decimal value represented by big-endian 128 bit signed integer.
- Decimal(t Type) (v [16]byte)
-
- // UnwrapDecimal returns decimal value represented by big-endian 128 bit signed
- // integer and its types information.
- UnwrapDecimal() Decimal
- IsDecimal() bool
- Err() error
-}
-
-// Scanner scanning raw ydb types
-type Scanner interface {
- // UnmarshalYDB must be implemented on client-side for unmarshal raw ydb value.
- UnmarshalYDB(raw RawValue) error
-}
+type (
+ RawValue = scanner.RawValue
+ Scanner = scanner.Scanner
+)
diff --git a/table/types/types_test.go b/table/types/types_test.go
deleted file mode 100644
index 9d4d46c4d..000000000
--- a/table/types/types_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package types
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestEqual(t *testing.T) {
- tests := []struct {
- lhs Type
- rhs Type
- equal bool
- }{
- {
- TypeBool,
- TypeBool,
- true,
- },
- {
- TypeBool,
- TypeText,
- false,
- },
- {
- TypeText,
- TypeText,
- true,
- },
- {
- Optional(TypeBool),
- Optional(TypeBool),
- true,
- },
- {
- Optional(TypeBool),
- Optional(TypeText),
- false,
- },
- {
- Optional(TypeText),
- Optional(TypeText),
- true,
- },
- }
- for _, tt := range tests {
- t.Run("", func(t *testing.T) {
- if equal := Equal(tt.lhs, tt.rhs); equal != tt.equal {
- t.Errorf("Equal(%s, %s) = %v, want %v", tt.lhs, tt.rhs, equal, tt.equal)
- }
- })
- }
-}
-
-func TestOptionalInnerType(t *testing.T) {
- tests := []struct {
- src Type
- innerType Type
- isOptional bool
- }{
- {
- TypeBool,
- nil,
- false,
- },
- {
- TypeText,
- nil,
- false,
- },
- {
- Optional(TypeBool),
- TypeBool,
- true,
- },
- {
- Optional(TypeText),
- TypeText,
- true,
- },
- {
- Optional(Tuple(TypeText, TypeBool, TypeUint64, Optional(TypeInt64))),
- Tuple(TypeText, TypeBool, TypeUint64, Optional(TypeInt64)),
- true,
- },
- }
- for _, tt := range tests {
- t.Run("", func(t *testing.T) {
- optional, isOptional := tt.src.(interface {
- IsOptional()
- InnerType() Type
- })
- require.Equal(t, tt.isOptional, isOptional)
- var innerType Type
- if isOptional {
- innerType = optional.InnerType()
- }
- if tt.innerType == nil {
- require.Nil(t, innerType)
- } else {
- require.True(t, Equal(tt.innerType, innerType))
- }
- })
- }
-}
diff --git a/table/types/value.go b/table/types/value.go
index 75b252c00..9249c4e3c 100644
--- a/table/types/value.go
+++ b/table/types/value.go
@@ -1,7 +1,6 @@
package types
import (
- "fmt"
"math/big"
"time"
@@ -174,21 +173,7 @@ func ZeroValue(t Type) Value { return value.ZeroValue(t) }
func OptionalValue(v Value) Value { return value.OptionalValue(v) }
// Decimal supported in scanner API
-type Decimal struct {
- Bytes [16]byte
- Precision uint32
- Scale uint32
-}
-
-func (d *Decimal) String() string {
- v := decimal.FromInt128(d.Bytes, d.Precision, d.Scale)
-
- return decimal.Format(v, d.Precision, d.Scale)
-}
-
-func (d *Decimal) BigInt() *big.Int {
- return decimal.FromInt128(d.Bytes, d.Precision, d.Scale)
-}
+type Decimal = decimal.Decimal
// DecimalValue creates decimal value of given types t and value v.
// Note that Decimal.Bytes interpreted as big-endian int128.
@@ -267,480 +252,171 @@ func VariantValueTuple(v Value, i uint32, variantT Type) Value {
}
func NullableBoolValue(v *bool) Value {
- if v == nil {
- return NullValue(TypeBool)
- }
-
- return OptionalValue(BoolValue(*v))
+ return value.NullableBoolValue(v)
}
func NullableInt8Value(v *int8) Value {
- if v == nil {
- return NullValue(TypeInt8)
- }
-
- return OptionalValue(Int8Value(*v))
+ return value.NullableInt8Value(v)
}
func NullableInt16Value(v *int16) Value {
- if v == nil {
- return NullValue(TypeInt16)
- }
-
- return OptionalValue(Int16Value(*v))
+ return value.NullableInt16Value(v)
}
func NullableInt32Value(v *int32) Value {
- if v == nil {
- return NullValue(TypeInt32)
- }
-
- return OptionalValue(Int32Value(*v))
+ return value.NullableInt32Value(v)
}
func NullableInt64Value(v *int64) Value {
- if v == nil {
- return NullValue(TypeInt64)
- }
-
- return OptionalValue(Int64Value(*v))
+ return value.NullableInt64Value(v)
}
func NullableUint8Value(v *uint8) Value {
- if v == nil {
- return NullValue(TypeUint8)
- }
-
- return OptionalValue(Uint8Value(*v))
+ return value.NullableUint8Value(v)
}
func NullableUint16Value(v *uint16) Value {
- if v == nil {
- return NullValue(TypeUint16)
- }
-
- return OptionalValue(Uint16Value(*v))
+ return value.NullableUint16Value(v)
}
func NullableUint32Value(v *uint32) Value {
- if v == nil {
- return NullValue(TypeUint32)
- }
-
- return OptionalValue(Uint32Value(*v))
+ return value.NullableUint32Value(v)
}
func NullableUint64Value(v *uint64) Value {
- if v == nil {
- return NullValue(TypeUint64)
- }
-
- return OptionalValue(Uint64Value(*v))
+ return value.NullableUint64Value(v)
}
func NullableFloatValue(v *float32) Value {
- if v == nil {
- return NullValue(TypeFloat)
- }
-
- return OptionalValue(FloatValue(*v))
+ return value.NullableFloatValue(v)
}
func NullableDoubleValue(v *float64) Value {
- if v == nil {
- return NullValue(TypeDouble)
- }
-
- return OptionalValue(DoubleValue(*v))
+ return value.NullableDoubleValue(v)
}
func NullableDateValue(v *uint32) Value {
- if v == nil {
- return NullValue(TypeDate)
- }
-
- return OptionalValue(DateValue(*v))
+ return value.NullableDateValue(v)
}
func NullableDateValueFromTime(v *time.Time) Value {
- if v == nil {
- return NullValue(TypeDate)
- }
-
- return OptionalValue(DateValueFromTime(*v))
+ return value.NullableDateValueFromTime(v)
}
func NullableDatetimeValue(v *uint32) Value {
- if v == nil {
- return NullValue(TypeDatetime)
- }
-
- return OptionalValue(DatetimeValue(*v))
+ return value.NullableDatetimeValue(v)
}
func NullableDatetimeValueFromTime(v *time.Time) Value {
- if v == nil {
- return NullValue(TypeDatetime)
- }
-
- return OptionalValue(DatetimeValueFromTime(*v))
+ return value.NullableDatetimeValueFromTime(v)
}
func NullableTzDateValue(v *string) Value {
- if v == nil {
- return NullValue(TypeTzDate)
- }
-
- return OptionalValue(TzDateValue(*v))
+ return value.NullableTzDateValue(v)
}
func NullableTzDateValueFromTime(v *time.Time) Value {
- if v == nil {
- return NullValue(TypeTzDate)
- }
-
- return OptionalValue(TzDateValueFromTime(*v))
+ return value.NullableTzDateValueFromTime(v)
}
func NullableTzDatetimeValue(v *string) Value {
- if v == nil {
- return NullValue(TypeTzDatetime)
- }
-
- return OptionalValue(TzDatetimeValue(*v))
+ return value.NullableTzDatetimeValue(v)
}
func NullableTzDatetimeValueFromTime(v *time.Time) Value {
- if v == nil {
- return NullValue(TypeTzDatetime)
- }
-
- return OptionalValue(TzDatetimeValueFromTime(*v))
+ return value.NullableTzDatetimeValueFromTime(v)
}
func NullableTimestampValue(v *uint64) Value {
- if v == nil {
- return NullValue(TypeTimestamp)
- }
-
- return OptionalValue(TimestampValue(*v))
+ return value.NullableTimestampValue(v)
}
func NullableTimestampValueFromTime(v *time.Time) Value {
- if v == nil {
- return NullValue(TypeTimestamp)
- }
-
- return OptionalValue(TimestampValueFromTime(*v))
+ return value.NullableTimestampValueFromTime(v)
}
func NullableTzTimestampValue(v *string) Value {
- if v == nil {
- return NullValue(TypeTzTimestamp)
- }
-
- return OptionalValue(TzTimestampValue(*v))
+ return value.NullableTzTimestampValue(v)
}
func NullableTzTimestampValueFromTime(v *time.Time) Value {
- if v == nil {
- return NullValue(TypeTzTimestamp)
- }
-
- return OptionalValue(TzTimestampValueFromTime(*v))
+ return value.NullableTzTimestampValueFromTime(v)
}
// NullableIntervalValue makes Value which maybe nil or valued
//
// Deprecated: use NullableIntervalValueFromMicroseconds instead
func NullableIntervalValue(v *int64) Value {
- if v == nil {
- return NullValue(TypeInterval)
- }
-
- return OptionalValue(IntervalValue(*v))
+ return value.NullableIntervalValueFromMicroseconds(v)
}
func NullableIntervalValueFromMicroseconds(v *int64) Value {
- if v == nil {
- return NullValue(TypeInterval)
- }
-
- return OptionalValue(IntervalValueFromMicroseconds(*v))
+ return value.NullableIntervalValueFromMicroseconds(v)
}
func NullableIntervalValueFromDuration(v *time.Duration) Value {
- if v == nil {
- return NullValue(TypeInterval)
- }
-
- return OptionalValue(IntervalValueFromDuration(*v))
+ return value.NullableIntervalValueFromDuration(v)
}
// NullableStringValue
//
// Deprecated: use NullableBytesValue instead
func NullableStringValue(v *[]byte) Value {
- if v == nil {
- return NullValue(TypeBytes)
- }
-
- return OptionalValue(StringValue(*v))
+ return value.NullableBytesValue(v)
}
func NullableBytesValue(v *[]byte) Value {
- if v == nil {
- return NullValue(TypeBytes)
- }
-
- return OptionalValue(BytesValue(*v))
+ return value.NullableBytesValue(v)
}
func NullableStringValueFromString(v *string) Value {
- if v == nil {
- return NullValue(TypeBytes)
- }
-
- return OptionalValue(BytesValueFromString(*v))
+ return value.NullableBytesValueFromString(v)
}
func NullableBytesValueFromString(v *string) Value {
- if v == nil {
- return NullValue(TypeBytes)
- }
-
- return OptionalValue(BytesValueFromString(*v))
+ return value.NullableBytesValueFromString(v)
}
func NullableUTF8Value(v *string) Value {
- if v == nil {
- return NullValue(TypeText)
- }
-
- return OptionalValue(TextValue(*v))
+ return value.NullableTextValue(v)
}
func NullableTextValue(v *string) Value {
- if v == nil {
- return NullValue(TypeText)
- }
-
- return OptionalValue(TextValue(*v))
+ return value.NullableTextValue(v)
}
func NullableYSONValue(v *string) Value {
- if v == nil {
- return NullValue(TypeYSON)
- }
-
- return OptionalValue(YSONValue(*v))
+ return value.NullableYSONValue(v)
}
func NullableYSONValueFromBytes(v *[]byte) Value {
- if v == nil {
- return NullValue(TypeYSON)
- }
-
- return OptionalValue(YSONValueFromBytes(*v))
+ return value.NullableYSONValueFromBytes(v)
}
func NullableJSONValue(v *string) Value {
- if v == nil {
- return NullValue(TypeJSON)
- }
-
- return OptionalValue(JSONValue(*v))
+ return value.NullableJSONValue(v)
}
func NullableJSONValueFromBytes(v *[]byte) Value {
- if v == nil {
- return NullValue(TypeJSON)
- }
-
- return OptionalValue(JSONValueFromBytes(*v))
+ return value.NullableJSONValueFromBytes(v)
}
func NullableUUIDValue(v *[16]byte) Value {
- if v == nil {
- return NullValue(TypeUUID)
- }
-
- return OptionalValue(UUIDValue(*v))
+ return value.NullableUUIDValue(v)
}
func NullableJSONDocumentValue(v *string) Value {
- if v == nil {
- return NullValue(TypeJSONDocument)
- }
-
- return OptionalValue(JSONDocumentValue(*v))
+ return value.NullableJSONDocumentValue(v)
}
func NullableJSONDocumentValueFromBytes(v *[]byte) Value {
- if v == nil {
- return NullValue(TypeJSONDocument)
- }
-
- return OptionalValue(JSONDocumentValueFromBytes(*v))
+ return value.NullableJSONDocumentValueFromBytes(v)
}
func NullableDyNumberValue(v *string) Value {
- if v == nil {
- return NullValue(TypeDyNumber)
- }
-
- return OptionalValue(DyNumberValue(*v))
+ return value.NullableDyNumberValue(v)
}
-// Nullable makes optional value from nullable type
-// Warning: type interface will be replaced in the future with typed parameters pattern from go1.18
-//
-//nolint:gocyclo
func Nullable(t Type, v interface{}) Value {
- switch t {
- case TypeBool:
- return NullableBoolValue(v.(*bool))
- case TypeInt8:
- return NullableInt8Value(v.(*int8))
- case TypeUint8:
- return NullableUint8Value(v.(*uint8))
- case TypeInt16:
- return NullableInt16Value(v.(*int16))
- case TypeUint16:
- return NullableUint16Value(v.(*uint16))
- case TypeInt32:
- return NullableInt32Value(v.(*int32))
- case TypeUint32:
- return NullableUint32Value(v.(*uint32))
- case TypeInt64:
- return NullableInt64Value(v.(*int64))
- case TypeUint64:
- return NullableUint64Value(v.(*uint64))
- case TypeFloat:
- return NullableFloatValue(v.(*float32))
- case TypeDouble:
- return NullableDoubleValue(v.(*float64))
- case TypeDate:
- switch tt := v.(type) {
- case *uint32:
- return NullableDateValue(tt)
- case *time.Time:
- return NullableDateValueFromTime(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeDate", tt))
- }
- case TypeDatetime:
- switch tt := v.(type) {
- case *uint32:
- return NullableDatetimeValue(tt)
- case *time.Time:
- return NullableDatetimeValueFromTime(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeDatetime", tt))
- }
- case TypeTimestamp:
- switch tt := v.(type) {
- case *uint64:
- return NullableTimestampValue(tt)
- case *time.Time:
- return NullableTimestampValueFromTime(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeTimestamp", tt))
- }
- case TypeInterval:
- switch tt := v.(type) {
- case *int64:
- return NullableIntervalValueFromMicroseconds(tt)
- case *time.Duration:
- return NullableIntervalValueFromDuration(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeInterval", tt))
- }
- case TypeTzDate:
- switch tt := v.(type) {
- case *string:
- return NullableTzDateValue(tt)
- case *time.Time:
- return NullableTzDateValueFromTime(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeTzDate", tt))
- }
- case TypeTzDatetime:
- switch tt := v.(type) {
- case *string:
- return NullableTzDatetimeValue(tt)
- case *time.Time:
- return NullableTzDatetimeValueFromTime(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeTzDatetime", tt))
- }
- case TypeTzTimestamp:
- switch tt := v.(type) {
- case *string:
- return NullableTzTimestampValue(tt)
- case *time.Time:
- return NullableTzTimestampValueFromTime(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeTzTimestamp", tt))
- }
- case TypeBytes:
- switch tt := v.(type) {
- case *[]byte:
- return NullableBytesValue(tt)
- case *string:
- return NullableStringValueFromString(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeBytes", tt))
- }
- case TypeText:
- switch tt := v.(type) {
- case *string:
- return NullableTextValue(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeText", tt))
- }
- case TypeYSON:
- switch tt := v.(type) {
- case *string:
- return NullableYSONValue(tt)
- case *[]byte:
- return NullableYSONValueFromBytes(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeYSON", tt))
- }
- case TypeJSON:
- switch tt := v.(type) {
- case *string:
- return NullableJSONValue(tt)
- case *[]byte:
- return NullableJSONValueFromBytes(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeJSON", tt))
- }
- case TypeUUID:
- switch tt := v.(type) {
- case *[16]byte:
- return NullableUUIDValue(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeUUID", tt))
- }
- case TypeJSONDocument:
- switch tt := v.(type) {
- case *string:
- return NullableJSONDocumentValue(tt)
- case *[]byte:
- return NullableJSONDocumentValueFromBytes(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeJSONDocument", tt))
- }
- case TypeDyNumber:
- switch tt := v.(type) {
- case *string:
- return NullableDyNumberValue(tt)
- default:
- panic(fmt.Sprintf("unsupported type conversion from %T to TypeDyNumber", tt))
- }
- default:
- panic(fmt.Sprintf("unsupported type: %T", t))
- }
+ return value.Nullable(t, v)
}
diff --git a/table/types/value_test.go b/table/types/value_test.go
deleted file mode 100644
index 598272c09..000000000
--- a/table/types/value_test.go
+++ /dev/null
@@ -1,617 +0,0 @@
-package types
-
-import (
- "fmt"
- "reflect"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
- "google.golang.org/protobuf/proto"
-
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
- "github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
-)
-
-func TestNullable(t *testing.T) {
- for _, test := range []struct {
- name string
- t Type
- v interface{}
- exp Value
- }{
- {
- name: "bool",
- t: TypeBool,
- v: func(v bool) *bool { return &v }(true),
- exp: OptionalValue(BoolValue(true)),
- },
- {
- name: "nil bool",
- t: TypeBool,
- v: func() *bool { return nil }(),
- exp: NullValue(TypeBool),
- },
- {
- name: "int8",
- t: TypeInt8,
- v: func(v int8) *int8 { return &v }(123),
- exp: OptionalValue(Int8Value(123)),
- },
- {
- name: "nil int8",
- t: TypeInt8,
- v: func() *int8 { return nil }(),
- exp: NullValue(TypeInt8),
- },
- {
- name: "uint8",
- t: TypeUint8,
- v: func(v uint8) *uint8 { return &v }(123),
- exp: OptionalValue(Uint8Value(123)),
- },
- {
- name: "nil uint8",
- t: TypeUint8,
- v: func() *uint8 { return nil }(),
- exp: NullValue(TypeUint8),
- },
- {
- name: "int16",
- t: TypeInt16,
- v: func(v int16) *int16 { return &v }(123),
- exp: OptionalValue(Int16Value(123)),
- },
- {
- name: "nil int16",
- t: TypeInt16,
- v: func() *int16 { return nil }(),
- exp: NullValue(TypeInt16),
- },
- {
- name: "uint16",
- t: TypeUint16,
- v: func(v uint16) *uint16 { return &v }(123),
- exp: OptionalValue(Uint16Value(123)),
- },
- {
- name: "nil uint16",
- t: TypeUint16,
- v: func() *uint16 { return nil }(),
- exp: NullValue(TypeUint16),
- },
- {
- name: "int32",
- t: TypeInt32,
- v: func(v int32) *int32 { return &v }(123),
- exp: OptionalValue(Int32Value(123)),
- },
- {
- name: "nil int32",
- t: TypeInt32,
- v: func() *int32 { return nil }(),
- exp: NullValue(TypeInt32),
- },
- {
- name: "uint32",
- t: TypeUint32,
- v: func(v uint32) *uint32 { return &v }(123),
- exp: OptionalValue(Uint32Value(123)),
- },
- {
- name: "nil uint32",
- t: TypeUint32,
- v: func() *uint32 { return nil }(),
- exp: NullValue(TypeUint32),
- },
- {
- name: "int64",
- t: TypeInt64,
- v: func(v int64) *int64 { return &v }(123),
- exp: OptionalValue(Int64Value(123)),
- },
- {
- name: "nil int64",
- t: TypeInt64,
- v: func() *int64 { return nil }(),
- exp: NullValue(TypeInt64),
- },
- {
- name: "uint64",
- t: TypeUint64,
- v: func(v uint64) *uint64 { return &v }(123),
- exp: OptionalValue(Uint64Value(123)),
- },
- {
- name: "nil uint64",
- t: TypeUint64,
- v: func() *uint64 { return nil }(),
- exp: NullValue(TypeUint64),
- },
- {
- name: "float",
- t: TypeFloat,
- v: func(v float32) *float32 { return &v }(123),
- exp: OptionalValue(FloatValue(123)),
- },
- {
- name: "nil float",
- t: TypeFloat,
- v: func() *float32 { return nil }(),
- exp: NullValue(TypeFloat),
- },
- {
- name: "double",
- t: TypeDouble,
- v: func(v float64) *float64 { return &v }(123),
- exp: OptionalValue(DoubleValue(123)),
- },
- {
- name: "nil float",
- t: TypeDouble,
- v: func() *float64 { return nil }(),
- exp: NullValue(TypeDouble),
- },
- {
- name: "date from int32",
- t: TypeDate,
- v: func(v uint32) *uint32 { return &v }(123),
- exp: OptionalValue(DateValue(123)),
- },
- {
- name: "date from time.Time",
- t: TypeDate,
- v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
- exp: OptionalValue(DateValueFromTime(time.Unix(123, 456))),
- },
- {
- name: "nil date",
- t: TypeDate,
- v: func() *uint32 { return nil }(),
- exp: NullValue(TypeDate),
- },
- {
- name: "datetime from int32",
- t: TypeDatetime,
- v: func(v uint32) *uint32 { return &v }(123),
- exp: OptionalValue(DatetimeValue(123)),
- },
- {
- name: "datetime from time.Time",
- t: TypeDatetime,
- v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
- exp: OptionalValue(DatetimeValueFromTime(time.Unix(123, 456))),
- },
- {
- name: "nil datetime",
- t: TypeDatetime,
- v: func() *uint32 { return nil }(),
- exp: NullValue(TypeDatetime),
- },
- {
- name: "timestamp from int32",
- t: TypeTimestamp,
- v: func(v uint64) *uint64 { return &v }(123),
- exp: OptionalValue(TimestampValue(123)),
- },
- {
- name: "timestamp from time.Time",
- t: TypeTimestamp,
- v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
- exp: OptionalValue(TimestampValueFromTime(time.Unix(123, 456))),
- },
- {
- name: "nil timestamp",
- t: TypeTimestamp,
- v: func() *uint64 { return nil }(),
- exp: NullValue(TypeTimestamp),
- },
- {
- name: "tzDate from int32",
- t: TypeTzDate,
- v: func(v string) *string { return &v }(""),
- exp: OptionalValue(TzDateValue("")),
- },
- {
- name: "tzDate from time.Time",
- t: TypeTzDate,
- v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
- exp: OptionalValue(TzDateValueFromTime(time.Unix(123, 456))),
- },
- {
- name: "nil tzDate",
- t: TypeTzDate,
- v: func() *string { return nil }(),
- exp: NullValue(TypeTzDate),
- },
- {
- name: "interval from int64",
- t: TypeInterval,
- v: func(v int64) *int64 { return &v }(123),
- exp: OptionalValue(IntervalValueFromMicroseconds(123)),
- },
- {
- name: "interval from time.Time",
- t: TypeInterval,
- v: func(v time.Duration) *time.Duration { return &v }(time.Second),
- exp: OptionalValue(IntervalValueFromDuration(time.Second)),
- },
- {
- name: "nil interval",
- t: TypeInterval,
- v: func() *int64 { return nil }(),
- exp: NullValue(TypeInterval),
- },
- {
- name: "tzDatetime from int32",
- t: TypeTzDatetime,
- v: func(v string) *string { return &v }(""),
- exp: OptionalValue(TzDatetimeValue("")),
- },
- {
- name: "tzTzDatetime from time.Time",
- t: TypeTzDatetime,
- v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
- exp: OptionalValue(TzDatetimeValueFromTime(time.Unix(123, 456))),
- },
- {
- name: "nil tzTzDatetime",
- t: TypeTzDatetime,
- v: func() *string { return nil }(),
- exp: NullValue(TypeTzDatetime),
- },
- {
- name: "tzTimestamp from int32",
- t: TypeTzTimestamp,
- v: func(v string) *string { return &v }(""),
- exp: OptionalValue(TzTimestampValue("")),
- },
- {
- name: "TzTimestamp from time.Time",
- t: TypeTzTimestamp,
- v: func(v time.Time) *time.Time { return &v }(time.Unix(123, 456)),
- exp: OptionalValue(TzTimestampValueFromTime(time.Unix(123, 456))),
- },
- {
- name: "nil TzTimestamp",
- t: TypeTzTimestamp,
- v: func() *string { return nil }(),
- exp: NullValue(TypeTzTimestamp),
- },
- {
- name: "string",
- t: TypeBytes,
- v: func(v string) *string { return &v }("test"),
- exp: OptionalValue(BytesValueFromString("test")),
- },
- {
- name: "string",
- t: TypeBytes,
- v: func(v []byte) *[]byte { return &v }([]byte("test")),
- exp: OptionalValue(BytesValueFromString("test")),
- },
- {
- name: "nil string",
- t: TypeBytes,
- v: func() *string { return nil }(),
- exp: NullValue(TypeBytes),
- },
- {
- name: "utf8",
- t: TypeText,
- v: func(v string) *string { return &v }("test"),
- exp: OptionalValue(TextValue("test")),
- },
- {
- name: "nil utf8",
- t: TypeText,
- v: func() *string { return nil }(),
- exp: NullValue(TypeText),
- },
- {
- name: "yson",
- t: TypeYSON,
- v: func(v string) *string { return &v }("test"),
- exp: OptionalValue(YSONValue("test")),
- },
- {
- name: "yson",
- t: TypeYSON,
- v: func(v []byte) *[]byte { return &v }([]byte("test")),
- exp: OptionalValue(YSONValueFromBytes([]byte("test"))),
- },
- {
- name: "nil yson",
- t: TypeYSON,
- v: func() *string { return nil }(),
- exp: NullValue(TypeYSON),
- },
- {
- name: "json",
- t: TypeJSON,
- v: func(v string) *string { return &v }("test"),
- exp: OptionalValue(JSONValue("test")),
- },
- {
- name: "json",
- t: TypeJSON,
- v: func(v []byte) *[]byte { return &v }([]byte("test")),
- exp: OptionalValue(JSONValueFromBytes([]byte("test"))),
- },
- {
- name: "nil json",
- t: TypeJSON,
- v: func() *string { return nil }(),
- exp: NullValue(TypeJSON),
- },
- {
- name: "uuid",
- t: TypeUUID,
- v: func(v [16]byte) *[16]byte { return &v }([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
- exp: OptionalValue(UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})),
- },
- {
- name: "jsonDocument",
- t: TypeJSONDocument,
- v: func(v string) *string { return &v }("test"),
- exp: OptionalValue(JSONDocumentValue("test")),
- },
- {
- name: "jsonDocument",
- t: TypeJSONDocument,
- v: func(v []byte) *[]byte { return &v }([]byte("test")),
- exp: OptionalValue(JSONDocumentValueFromBytes([]byte("test"))),
- },
- {
- name: "nil jsonDocument",
- t: TypeJSONDocument,
- v: func() *string { return nil }(),
- exp: NullValue(TypeJSONDocument),
- },
- {
- name: "dyNumber",
- t: TypeDyNumber,
- v: func(v string) *string { return &v }("test"),
- exp: OptionalValue(DyNumberValue("test")),
- },
- {
- name: "nil dyNumber",
- t: TypeDyNumber,
- v: func() *string { return nil }(),
- exp: NullValue(TypeDyNumber),
- },
- } {
- t.Run(test.name, func(t *testing.T) {
- a := allocator.New()
- defer a.Free()
- v := Nullable(test.t, test.v)
- if !proto.Equal(value.ToYDB(v, a), value.ToYDB(test.exp, a)) {
- t.Fatalf("unexpected value: %v, exp: %v", v, test.exp)
- }
- })
- }
-}
-
-func TestCastNumbers(t *testing.T) {
- numberValues := []struct {
- value Value
- signed bool
- len int
- }{
- {
- value: Uint64Value(1),
- signed: false,
- len: 8,
- },
- {
- value: Int64Value(2),
- signed: true,
- len: 8,
- },
- {
- value: Uint32Value(3),
- signed: false,
- len: 4,
- },
- {
- value: Int32Value(4),
- signed: true,
- len: 4,
- },
- {
- value: Uint16Value(5),
- signed: false,
- len: 2,
- },
- {
- value: Int16Value(6),
- signed: true,
- len: 2,
- },
- {
- value: Uint8Value(7),
- signed: false,
- len: 1,
- },
- {
- value: Int8Value(8),
- signed: true,
- len: 1,
- },
- }
- numberDestinations := []struct {
- destination interface{}
- signed bool
- len int
- }{
- {
- destination: func(v uint64) *uint64 { return &v }(1),
- signed: false,
- len: 8,
- },
- {
- destination: func(v int64) *int64 { return &v }(2),
- signed: true,
- len: 8,
- },
- {
- destination: func(v uint32) *uint32 { return &v }(3),
- signed: false,
- len: 4,
- },
- {
- destination: func(v int32) *int32 { return &v }(4),
- signed: true,
- len: 4,
- },
- {
- destination: func(v uint16) *uint16 { return &v }(5),
- signed: false,
- len: 2,
- },
- {
- destination: func(v int16) *int16 { return &v }(6),
- signed: true,
- len: 2,
- },
- {
- destination: func(v uint8) *uint8 { return &v }(7),
- signed: false,
- len: 1,
- },
- {
- destination: func(v int8) *int8 { return &v }(8),
- signed: true,
- len: 1,
- },
- {
- destination: func(v float32) *float32 { return &v }(7),
- signed: true,
- len: 4,
- },
- {
- destination: func(v float64) *float64 { return &v }(8),
- signed: true,
- len: 8,
- },
- }
- for _, dst := range numberDestinations {
- t.Run(reflect.ValueOf(dst.destination).Type().Elem().String(), func(t *testing.T) {
- for _, src := range numberValues {
- t.Run(src.value.Yql(), func(t *testing.T) {
- mustErr := false
- switch {
- case src.len == dst.len && src.signed != dst.signed,
- src.len > dst.len,
- src.signed && !dst.signed:
- mustErr = true
- }
- err := CastTo(src.value, dst.destination)
- if mustErr {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- }
- })
- t.Run(OptionalValue(src.value).Yql(), func(t *testing.T) {
- mustErr := false
- switch {
- case src.len == dst.len && src.signed != dst.signed,
- src.len > dst.len,
- src.signed && !dst.signed:
- mustErr = true
- }
- err := CastTo(OptionalValue(src.value), dst.destination)
- if mustErr {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- }
- })
- }
- })
- }
-}
-
-func TestCastOtherTypes(t *testing.T) {
- for _, tt := range []struct {
- v Value
- dst interface{}
- result interface{}
- error bool
- }{
- {
- v: BytesValue([]byte("test")),
- dst: func(v []byte) *[]byte { return &v }(make([]byte, 0, 10)),
- result: func(v []byte) *[]byte { return &v }([]byte("test")),
- error: false,
- },
- {
- v: TextValue("test"),
- dst: func(v []byte) *[]byte { return &v }(make([]byte, 0, 10)),
- result: func(v []byte) *[]byte { return &v }([]byte("test")),
- error: false,
- },
- {
- v: BytesValue([]byte("test")),
- dst: func(v string) *string { return &v }(""),
- result: func(v string) *string { return &v }("test"),
- error: false,
- },
- {
- v: DoubleValue(123),
- dst: func(v float64) *float64 { return &v }(9),
- result: func(v float64) *float64 { return &v }(123),
- error: false,
- },
- {
- v: DoubleValue(123),
- dst: func(v float32) *float32 { return &v }(9),
- result: func(v float32) *float32 { return &v }(9),
- error: true,
- },
- {
- v: FloatValue(123),
- dst: func(v float64) *float64 { return &v }(9),
- result: func(v float64) *float64 { return &v }(123),
- error: false,
- },
- {
- v: FloatValue(123),
- dst: func(v float32) *float32 { return &v }(9),
- result: func(v float32) *float32 { return &v }(123),
- error: false,
- },
- {
- v: Uint64Value(123),
- dst: func(v float32) *float32 { return &v }(9),
- result: func(v float32) *float32 { return &v }(9),
- error: true,
- },
- {
- v: Uint64Value(123),
- dst: func(v float64) *float64 { return &v }(9),
- result: func(v float64) *float64 { return &v }(9),
- error: true,
- },
- {
- v: OptionalValue(DoubleValue(123)),
- dst: func(v float64) *float64 { return &v }(9),
- result: func(v float64) *float64 { return &v }(123),
- error: false,
- },
- } {
- t.Run(fmt.Sprintf("cast %s to %v", tt.v.Type().Yql(), reflect.ValueOf(tt.dst).Type().Elem()),
- func(t *testing.T) {
- if err := CastTo(tt.v, tt.dst); (err != nil) != tt.error {
- t.Errorf("castTo() error = %v, want %v", err, tt.error)
- } else if !reflect.DeepEqual(tt.dst, tt.result) {
- t.Errorf("castTo() result = %+v, want %+v",
- reflect.ValueOf(tt.dst).Elem(),
- reflect.ValueOf(tt.result).Elem(),
- )
- }
- },
- )
- }
-}
diff --git a/tests/integration/basic_example_native_test.go b/tests/integration/basic_example_native_test.go
index f6f4b7dde..1e4231e5f 100644
--- a/tests/integration/basic_example_native_test.go
+++ b/tests/integration/basic_example_native_test.go
@@ -232,7 +232,7 @@ func TestBasicExampleNative(t *testing.T) { //nolint:gocyclo
db, err := ydb.Open(ctx,
os.Getenv("YDB_CONNECTION_STRING"),
ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
- ydb.WithUserAgent("table/e2e"),
+ ydb.WithApplicationName("table/e2e"),
withMetrics(t, trace.DetailsAll, time.Second),
ydb.With(
config.WithOperationTimeout(time.Second*5),
diff --git a/tests/integration/connection_test.go b/tests/integration/connection_test.go
index d4aecc63c..fcbcfb837 100644
--- a/tests/integration/connection_test.go
+++ b/tests/integration/connection_test.go
@@ -46,7 +46,7 @@ func TestConnection(t *testing.T) {
if !has {
t.Fatalf("no medatada")
}
- userAgents := md.Get(meta.HeaderUserAgent)
+ userAgents := md.Get(meta.HeaderApplicationName)
if len(userAgents) == 0 {
t.Fatalf("no user agent")
}
@@ -87,7 +87,7 @@ func TestConnection(t *testing.T) {
newLoggerWithMinLevel(t, log.WARN),
trace.MatchDetails(`ydb\.(driver|discovery|retry|scheme).*`),
),
- ydb.WithUserAgent(userAgent),
+ ydb.WithApplicationName(userAgent),
ydb.WithRequestsType(requestType),
ydb.With(
config.WithGrpcOptions(
diff --git a/tests/integration/connection_with_compression_test.go b/tests/integration/connection_with_compression_test.go
index cdb15ddac..c0be662b4 100644
--- a/tests/integration/connection_with_compression_test.go
+++ b/tests/integration/connection_with_compression_test.go
@@ -45,7 +45,7 @@ func TestConnectionWithCompression(t *testing.T) {
if !has {
t.Fatalf("no medatada")
}
- userAgents := md.Get(meta.HeaderUserAgent)
+ userAgents := md.Get(meta.HeaderApplicationName)
if len(userAgents) == 0 {
t.Fatalf("no user agent")
}
@@ -79,7 +79,7 @@ func TestConnectionWithCompression(t *testing.T) {
newLoggerWithMinLevel(t, log.WARN),
trace.MatchDetails(`ydb\.(driver|discovery|retry|scheme).*`),
),
- ydb.WithUserAgent(userAgent),
+ ydb.WithApplicationName(userAgent),
ydb.WithRequestsType(requestType),
ydb.With(
config.WithGrpcOptions(
diff --git a/tests/integration/coordination_test.go b/tests/integration/coordination_test.go
new file mode 100644
index 000000000..c3551e604
--- /dev/null
+++ b/tests/integration/coordination_test.go
@@ -0,0 +1,102 @@
+//go:build integration
+// +build integration
+
+package integration
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination"
+ "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options"
+ "github.com/ydb-platform/ydb-go-sdk/v3/log"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+//nolint:errcheck
+func TestCoordinationSemaphore(t *testing.T) {
+ ctx := context.TODO()
+ db, err := ydb.Open(ctx,
+ os.Getenv("YDB_CONNECTION_STRING"),
+ ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
+ ydb.WithLogger(
+ log.Default(os.Stderr, log.WithMinLevel(log.TRACE)),
+ trace.MatchDetails(`ydb\.(coordination).*`),
+ ),
+ )
+ if err != nil {
+ t.Fatalf("failed to connect: %v", err)
+ }
+ defer db.Close(ctx) // cleanup resources
+
+ const nodePath = "/local/coordination/node/test"
+
+ // create node
+ err = db.Coordination().CreateNode(ctx, nodePath, coordination.NodeConfig{
+ Path: "",
+ SelfCheckPeriodMillis: 1000,
+ SessionGracePeriodMillis: 1000,
+ ReadConsistencyMode: coordination.ConsistencyModeStrict,
+ AttachConsistencyMode: coordination.ConsistencyModeStrict,
+ RatelimiterCountersMode: coordination.RatelimiterCountersModeDetailed,
+ })
+ if err != nil {
+ t.Fatalf("failed to create node: %v", err)
+ }
+ defer db.Coordination().DropNode(ctx, nodePath)
+ e, c, err := db.Coordination().DescribeNode(ctx, nodePath)
+ if err != nil {
+ t.Fatalf("failed to describe node: %v\n", err)
+ }
+ fmt.Printf("node description: %+v\nnode config: %+v\n", e, c)
+
+ s, err := db.Coordination().Session(ctx, nodePath)
+ if err != nil {
+ t.Fatalf("failed to create session: %v\n", err)
+ }
+ defer s.Close(ctx)
+ fmt.Printf("session 1 created, id: %d\n", s.SessionID())
+
+ err = s.CreateSemaphore(ctx, "my-semaphore", 20, options.WithCreateData([]byte{1, 2, 3}))
+ if err != nil {
+ t.Fatalf("failed to create semaphore: %v", err)
+ }
+ fmt.Printf("semaphore my-semaphore created\n")
+
+ lease, err := s.AcquireSemaphore(ctx, "my-semaphore", 10)
+ if err != nil {
+ t.Fatalf("failed to acquire semaphore: %v", err)
+ }
+ defer lease.Release()
+ fmt.Printf("session 1 acquired semaphore 10\n")
+
+ s.Reconnect()
+ fmt.Printf("session 1 reconnected\n")
+
+ desc, err := s.DescribeSemaphore(
+ ctx,
+ "my-semaphore",
+ options.WithDescribeOwners(true),
+ options.WithDescribeWaiters(true),
+ )
+ if err != nil {
+ t.Fatalf("failed to describe semaphore: %v", err)
+ }
+ fmt.Printf("session 1 described semaphore %v\n", desc)
+
+ err = lease.Release()
+ if err != nil {
+ t.Fatalf("failed to release semaphore: %v", err)
+ }
+ fmt.Printf("session 1 released semaphore my-semaphore\n")
+
+ err = s.DeleteSemaphore(ctx, "my-semaphore", options.WithForceDelete(true))
+ if err != nil {
+ t.Fatalf("failed to delete semaphore: %v", err)
+ }
+
+ fmt.Printf("deleted semaphore my-semaphore\n")
+}
diff --git a/tests/integration/database_sql_containers_test.go b/tests/integration/database_sql_containers_test.go
index bbb8cc2db..5e8ae71f8 100644
--- a/tests/integration/database_sql_containers_test.go
+++ b/tests/integration/database_sql_containers_test.go
@@ -149,7 +149,7 @@ func (s *testDatabaseSqlContainersExampleStruct) Scan(res interface{}) error {
return nil
}
}
- return fmt.Errorf("type '%T' is not a `types.Value` type", res)
+ return fmt.Errorf("type '%T' is not a `types.value` type", res)
}
type testDatabaseSqlContainersExampleList []string
diff --git a/tests/integration/discovery_test.go b/tests/integration/discovery_test.go
index 410605bbf..511043ebd 100644
--- a/tests/integration/discovery_test.go
+++ b/tests/integration/discovery_test.go
@@ -31,12 +31,12 @@ func TestDiscovery(t *testing.T) {
if !has {
t.Fatalf("no medatada")
}
- userAgents := md.Get(meta.HeaderUserAgent)
- if len(userAgents) == 0 {
- t.Fatalf("no user agent")
+ applicationName := md.Get(meta.HeaderApplicationName)
+ if len(applicationName) == 0 {
+ t.Fatalf("no application name")
}
- if userAgents[0] != userAgent {
- t.Fatalf("unknown user agent: %s", userAgents[0])
+ if applicationName[0] != userAgent {
+ t.Fatalf("unknown user agent: %s", applicationName[0])
}
requestTypes := md.Get(meta.HeaderRequestType)
if len(requestTypes) == 0 {
@@ -66,7 +66,7 @@ func TestDiscovery(t *testing.T) {
newLoggerWithMinLevel(t, log.WARN),
trace.MatchDetails(`ydb\.(driver|discovery|repeater).*`),
),
- ydb.WithUserAgent(userAgent),
+ ydb.WithApplicationName(userAgent),
ydb.WithRequestsType(requestType),
ydb.With(
config.WithGrpcOptions(
diff --git a/tests/integration/helpers_test.go b/tests/integration/helpers_test.go
index 9184ad86c..73363250d 100644
--- a/tests/integration/helpers_test.go
+++ b/tests/integration/helpers_test.go
@@ -275,7 +275,9 @@ func (scope *scopeT) TableName(opts ...func(t *tableNameParams)) string {
`,
}
for _, opt := range opts {
- opt(¶ms)
+ if opt != nil {
+ opt(¶ms)
+ }
}
return scope.Cache(params.tableName, nil, func() (res interface{}, err error) {
err = scope.Driver().Table().Do(scope.Ctx, func(ctx context.Context, s table.Session) (err error) {
diff --git a/tests/integration/query_execute_test.go b/tests/integration/query_execute_test.go
new file mode 100644
index 000000000..81687bae8
--- /dev/null
+++ b/tests/integration/query_execute_test.go
@@ -0,0 +1,276 @@
+//go:build integration
+// +build integration
+
+package integration
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
+ "github.com/ydb-platform/ydb-go-sdk/v3/log"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+func TestQueryExecute(t *testing.T) {
+ if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
+ t.Skip("query service not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
+ }
+
+ ctx, cancel := context.WithCancel(xtest.Context(t))
+ defer cancel()
+
+ db, err := ydb.Open(ctx,
+ os.Getenv("YDB_CONNECTION_STRING"),
+ ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
+ ydb.WithSessionPoolSizeLimit(10),
+ ydb.WithTraceQuery(
+ log.Query(
+ log.Default(os.Stdout,
+ log.WithLogQuery(),
+ log.WithColoring(),
+ log.WithMinLevel(log.INFO),
+ ),
+ trace.QueryEvents,
+ ),
+ ),
+ )
+ require.NoError(t, err)
+ t.Run("Stats", func(t *testing.T) {
+ s, err := query.Stats(db.Query())
+ require.NoError(t, err)
+ require.EqualValues(t, 10, s.Limit)
+ })
+ t.Run("Scan", func(t *testing.T) {
+ var (
+ p1 string
+ p2 uint64
+ p3 time.Duration
+ )
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ _, res, err := s.Execute(ctx, `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT $p1, $p2, $p3;
+ `,
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$p1").Text("test").
+ Param("$p2").Uint64(100500000000).
+ Param("$p3").Interval(time.Duration(100500000000)).
+ Build(),
+ ),
+ query.WithSyntax(query.SyntaxYQL),
+ )
+ if err != nil {
+ return err
+ }
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ err = row.Scan(&p1, &p2, &p3)
+ if err != nil {
+ return err
+ }
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ require.EqualValues(t, "test", p1)
+ require.EqualValues(t, 100500000000, p2)
+ require.EqualValues(t, time.Duration(100500000000), p3)
+ })
+ t.Run("ScanNamed", func(t *testing.T) {
+ var (
+ p1 string
+ p2 uint64
+ p3 time.Duration
+ )
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ _, res, err := s.Execute(ctx, `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT $p1 AS p1, $p2 AS p2, $p3 AS p3;
+ `,
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$p1").Text("test").
+ Param("$p2").Uint64(100500000000).
+ Param("$p3").Interval(time.Duration(100500000000)).
+ Build(),
+ ),
+ query.WithSyntax(query.SyntaxYQL),
+ )
+ if err != nil {
+ return err
+ }
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ err = row.ScanNamed(
+ query.Named("p1", &p1),
+ query.Named("p2", &p2),
+ query.Named("p3", &p3),
+ )
+ if err != nil {
+ return err
+ }
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ require.EqualValues(t, "test", p1)
+ require.EqualValues(t, 100500000000, p2)
+ require.EqualValues(t, time.Duration(100500000000), p3)
+ })
+ t.Run("ScanStruct", func(t *testing.T) {
+ var data struct {
+ P1 *string `sql:"p1"`
+ P2 uint64 `sql:"p2"`
+ P3 time.Duration `sql:"p3"`
+ P4 *string `sql:"p4"`
+ }
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ _, res, err := s.Execute(ctx, `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT CAST($p1 AS Optional) AS p1, $p2 AS p2, $p3 AS p3, CAST(NULL AS Optional) AS p4;
+ `,
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$p1").Text("test").
+ Param("$p2").Uint64(100500000000).
+ Param("$p3").Interval(time.Duration(100500000000)).
+ Build(),
+ ),
+ query.WithSyntax(query.SyntaxYQL),
+ )
+ if err != nil {
+ return err
+ }
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ err = row.ScanStruct(&data)
+ if err != nil {
+ return err
+ }
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ require.NotNil(t, data.P1)
+ require.EqualValues(t, "test", *data.P1)
+ require.EqualValues(t, 100500000000, data.P2)
+ require.EqualValues(t, time.Duration(100500000000), data.P3)
+ require.Nil(t, data.P4)
+ })
+ t.Run("Tx", func(t *testing.T) {
+ t.Run("Explicit", func(t *testing.T) {
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ tx, err := s.Begin(ctx, query.TxSettings(query.WithSerializableReadWrite()))
+ if err != nil {
+ return err
+ }
+ res, err := tx.Execute(ctx, `SELECT 1`)
+ if err != nil {
+ return err
+ }
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ var v int32
+ err = row.Scan(&v)
+ if err != nil {
+ return err
+ }
+ if v != 1 {
+ return fmt.Errorf("unexpected value from database: %d", v)
+ }
+ if err = res.Err(); err != nil {
+ return err
+ }
+ return tx.CommitTx(ctx)
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ })
+ t.Run("Lazy", func(t *testing.T) {
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ tx, res, err := s.Execute(ctx, `SELECT 1`,
+ query.WithTxControl(query.TxControl(query.BeginTx(query.WithSerializableReadWrite()))),
+ )
+ if err != nil {
+ return err
+ }
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ var v int32
+ err = row.Scan(&v)
+ if err != nil {
+ return err
+ }
+ if v != 1 {
+ return fmt.Errorf("unexpected value from database: %d", v)
+ }
+ if err = res.Err(); err != nil {
+ return err
+ }
+ res, err = tx.Execute(ctx, `SELECT 2`, query.WithCommit())
+ if err != nil {
+ return err
+ }
+ rs, err = res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err = rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ err = row.Scan(&v)
+ if err != nil {
+ return err
+ }
+ if v != 2 {
+ return fmt.Errorf("unexpected value from database: %d", v)
+ }
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ })
+ })
+}
diff --git a/tests/integration/query_tx_execute_test.go b/tests/integration/query_tx_execute_test.go
new file mode 100644
index 000000000..91a70029a
--- /dev/null
+++ b/tests/integration/query_tx_execute_test.go
@@ -0,0 +1,64 @@
+//go:build integration
+// +build integration
+
+package integration
+
+import (
+ "context"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
+ "github.com/ydb-platform/ydb-go-sdk/v3/log"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+)
+
+func TestQueryTxExecute(t *testing.T) {
+ if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
+ t.Skip("query service not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ db, err := ydb.Open(ctx,
+ os.Getenv("YDB_CONNECTION_STRING"),
+ ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
+ ydb.WithTraceQuery(
+ log.Query(
+ log.Default(os.Stdout,
+ log.WithLogQuery(),
+ log.WithColoring(),
+ log.WithMinLevel(log.INFO),
+ ),
+ trace.QueryEvents,
+ ),
+ ),
+ )
+ require.NoError(t, err)
+ err = db.Query().DoTx(ctx, func(ctx context.Context, tx query.TxActor) (err error) {
+ res, err := tx.Execute(ctx, "SELECT 1 AS col1")
+ if err != nil {
+ return err
+ }
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ return err
+ }
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ return err
+ }
+ var col1 int
+ err = row.ScanNamed(query.Named("col1", &col1))
+ if err != nil {
+ return err
+ }
+ return res.Err()
+ }, query.WithIdempotent(), query.WithTxSettings(query.TxSettings(query.WithSerializableReadWrite())))
+ require.NoError(t, err)
+}
diff --git a/tests/integration/scripting_test.go b/tests/integration/scripting_test.go
index fb3cc9d49..b1c908e0d 100644
--- a/tests/integration/scripting_test.go
+++ b/tests/integration/scripting_test.go
@@ -40,7 +40,7 @@ func TestScripting(t *testing.T) {
newLogger(t),
trace.MatchDetails(`ydb\.(driver|discovery|retry|scheme).*`),
),
- ydb.WithUserAgent("scripting"),
+ ydb.WithApplicationName("scripting"),
)
if err != nil {
t.Fatal(err)
diff --git a/tests/integration/sugar_result_test.go b/tests/integration/sugar_result_test.go
new file mode 100644
index 000000000..ba863dd72
--- /dev/null
+++ b/tests/integration/sugar_result_test.go
@@ -0,0 +1,248 @@
+//go:build integration
+// +build integration
+
+package integration
+
+import (
+ "context"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/sugar"
+ "github.com/ydb-platform/ydb-go-sdk/v3/table"
+ "github.com/ydb-platform/ydb-go-sdk/v3/table/result/named"
+ "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
+)
+
+func TestSugarResult(t *testing.T) {
+ if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
+ t.Skip("query service not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ db, err := ydb.Open(ctx,
+ os.Getenv("YDB_CONNECTION_STRING"),
+ ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
+ )
+ require.NoError(t, err)
+ t.Run("Scan", func(t *testing.T) {
+ t.Run("Table", func(t *testing.T) {
+ var (
+ p1 string
+ p2 uint64
+ p3 time.Duration
+ )
+ err = db.Table().Do(ctx, func(ctx context.Context, s table.Session) (err error) {
+ _, res, err := s.Execute(ctx, table.DefaultTxControl(), `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT $p1, $p2, $p3;
+ `,
+ table.NewQueryParameters(
+ table.ValueParam("$p1", types.TextValue("test")),
+ table.ValueParam("$p2", types.Uint64Value(100500000000)),
+ table.ValueParam("$p3", types.IntervalValueFromDuration(time.Duration(100500000000))),
+ ),
+ )
+ if err != nil {
+ return err
+ }
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ if err = res.Scan(&p1, &p2, &p3); err != nil {
+ return err
+ }
+ }
+ }
+
+ return res.Err()
+ }, table.WithIdempotent())
+ require.NoError(t, err)
+ require.EqualValues(t, "test", p1)
+ require.EqualValues(t, 100500000000, p2)
+ require.EqualValues(t, time.Duration(100500000000), p3)
+ })
+ t.Run("Sugar", func(t *testing.T) {
+ var (
+ p1 string
+ p2 uint64
+ p3 time.Duration
+ )
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ _, r, err := s.Execute(ctx, `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT $p1, $p2, $p3;
+ `,
+ query.WithParameters(
+ table.NewQueryParameters(
+ table.ValueParam("$p1", types.TextValue("test")),
+ table.ValueParam("$p2", types.Uint64Value(100500000000)),
+ table.ValueParam("$p3", types.IntervalValueFromDuration(time.Duration(100500000000))),
+ ),
+ ),
+ )
+ if err != nil {
+ return err
+ }
+ res := sugar.Result(r)
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ if err = res.Scan(&p1, &p2, &p3); err != nil {
+ return err
+ }
+ }
+ }
+
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ require.EqualValues(t, "test", p1)
+ require.EqualValues(t, 100500000000, p2)
+ require.EqualValues(t, time.Duration(100500000000), p3)
+ })
+ })
+ t.Run("ScanNamed", func(t *testing.T) {
+ t.Run("Table", func(t *testing.T) {
+ var (
+ p1 string
+ p2 uint64
+ p3 time.Duration
+ )
+ err = db.Table().Do(ctx, func(ctx context.Context, s table.Session) (err error) {
+ _, res, err := s.Execute(ctx, table.DefaultTxControl(), `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT $p1 AS p1, $p2 AS p2, $p3 AS p3;
+ `,
+ table.NewQueryParameters(
+ table.ValueParam("$p1", types.TextValue("test")),
+ table.ValueParam("$p2", types.Uint64Value(100500000000)),
+ table.ValueParam("$p3", types.IntervalValueFromDuration(time.Duration(100500000000))),
+ ),
+ )
+ if err != nil {
+ return err
+ }
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ if err = res.ScanNamed(
+ named.Required("p1", &p1),
+ named.Required("p2", &p2),
+ named.Required("p3", &p3),
+ ); err != nil {
+ return err
+ }
+ }
+ }
+
+ return res.Err()
+ }, table.WithIdempotent())
+ require.NoError(t, err)
+ require.EqualValues(t, "test", p1)
+ require.EqualValues(t, 100500000000, p2)
+ require.EqualValues(t, time.Duration(100500000000), p3)
+ })
+ t.Run("Sugar", func(t *testing.T) {
+ var (
+ p1 string
+ p2 uint64
+ p3 time.Duration
+ )
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ _, r, err := s.Execute(ctx, `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT $p1 AS p1, $p2 AS p2, $p3 AS p3;
+ `,
+ query.WithParameters(
+ table.NewQueryParameters(
+ table.ValueParam("$p1", types.TextValue("test")),
+ table.ValueParam("$p2", types.Uint64Value(100500000000)),
+ table.ValueParam("$p3", types.IntervalValueFromDuration(time.Duration(100500000000))),
+ ),
+ ),
+ )
+ if err != nil {
+ return err
+ }
+ res := sugar.Result(r)
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ if err = res.ScanNamed(
+ named.Required("p1", &p1),
+ named.Required("p2", &p2),
+ named.Required("p3", &p3),
+ ); err != nil {
+ return err
+ }
+ }
+ }
+
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ require.EqualValues(t, "test", p1)
+ require.EqualValues(t, 100500000000, p2)
+ require.EqualValues(t, time.Duration(100500000000), p3)
+ })
+ })
+ t.Run("ScanStruct", func(t *testing.T) {
+ t.Run("Sugar", func(t *testing.T) {
+ var data struct {
+ P1 *string `sql:"p1"`
+ P2 uint64 `sql:"p2"`
+ P3 time.Duration `sql:"p3"`
+ P4 *string `sql:"p4"`
+ }
+ err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
+ _, r, err := s.Execute(ctx, `
+ DECLARE $p1 AS Text;
+ DECLARE $p2 AS Uint64;
+ DECLARE $p3 AS Interval;
+ SELECT CAST($p1 AS Optional) AS p1, $p2 AS p2, $p3 AS p3, CAST(NULL AS Optional) AS p4;
+ `,
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$p1").Text("test").
+ Param("$p2").Uint64(100500000000).
+ Param("$p3").Interval(time.Duration(100500000000)).
+ Build(),
+ ),
+ query.WithSyntax(query.SyntaxYQL),
+ )
+ if err != nil {
+ return err
+ }
+ res := sugar.Result(r)
+ for res.NextResultSet(ctx) {
+ for res.NextRow() {
+ if err = res.ScanStruct(&data); err != nil {
+ return err
+ }
+ }
+ }
+
+ return res.Err()
+ }, query.WithIdempotent())
+ require.NoError(t, err)
+ require.NotNil(t, data.P1)
+ require.EqualValues(t, "test", *data.P1)
+ require.EqualValues(t, 100500000000, data.P2)
+ require.EqualValues(t, time.Duration(100500000000), data.P3)
+ require.Nil(t, data.P4)
+ })
+ })
+}
diff --git a/tests/integration/tx_test.go b/tests/integration/tx_test.go
index a3c3db081..3c5666116 100644
--- a/tests/integration/tx_test.go
+++ b/tests/integration/tx_test.go
@@ -138,14 +138,14 @@ func TestNoEffectsIfForgetCommitTx(t *testing.T) {
// check row for NO write
var (
- value string
- errConnAlreadyHaveTx *xsql.ErrConnAlreadyHaveTx
+ value string
+ connAlreadyHaveTxError *xsql.ConnAlreadyHaveTxError
)
err = db.QueryRowContext(ctx, `SELECT val FROM table WHERE id = $1`, id).Scan(&value)
require.ErrorIs(t, err, sql.ErrNoRows)
// second tx on existing conn === session
_, err = cc.BeginTx(ctx, &sql.TxOptions{})
- require.ErrorAs(t, err, &errConnAlreadyHaveTx)
+ require.ErrorAs(t, err, &connAlreadyHaveTxError)
})
}
diff --git a/tests/integration/with_trace_retry_test.go b/tests/integration/with_trace_retry_test.go
index 42bc1958b..1cb5b6564 100644
--- a/tests/integration/with_trace_retry_test.go
+++ b/tests/integration/with_trace_retry_test.go
@@ -26,9 +26,7 @@ func TestWithTraceRetry(t *testing.T) {
scope = newScope(t)
db = scope.Driver(
ydb.WithTraceRetry(trace.Retry{
- OnRetry: func(
- info trace.RetryLoopStartInfo,
- ) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
retryCalled[info.Label] = true
return nil
},
@@ -64,9 +62,7 @@ func TestWithTraceRetry(t *testing.T) {
scope = newScope(t)
nativeDb = scope.Driver(
ydb.WithTraceRetry(trace.Retry{
- OnRetry: func(
- info trace.RetryLoopStartInfo,
- ) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
retryCalled[info.Label] = true
return nil
},
diff --git a/tests/slo/Dockerfile b/tests/slo/Dockerfile
index 743535336..49226b98d 100644
--- a/tests/slo/Dockerfile
+++ b/tests/slo/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.21 as build
+FROM golang:1.22 as build
ARG SRC_PATH
ARG JOB_NAME
COPY . /src
diff --git a/tests/slo/database/sql/storage.go b/tests/slo/database/sql/storage.go
index e04473c9a..8c740803c 100755
--- a/tests/slo/database/sql/storage.go
+++ b/tests/slo/database/sql/storage.go
@@ -139,11 +139,9 @@ func (s *Storage) Read(ctx context.Context, entryID generator.RowID) (res genera
retry.WithIdempotent(true),
retry.WithTrace(
&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopDoneInfo) {
- attempts = info.Attempts
- }
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
}
},
},
@@ -179,11 +177,9 @@ func (s *Storage) Write(ctx context.Context, e generator.Row) (attempts int, err
retry.WithIdempotent(true),
retry.WithTrace(
&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopDoneInfo) {
- attempts = info.Attempts
- }
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
}
},
},
diff --git a/tests/slo/go.mod b/tests/slo/go.mod
index b2c7d4951..e8f010142 100644
--- a/tests/slo/go.mod
+++ b/tests/slo/go.mod
@@ -5,8 +5,9 @@ go 1.21
require (
github.com/prometheus/client_golang v1.14.0
github.com/ydb-platform/gorm-driver v0.1.1
- github.com/ydb-platform/ydb-go-sdk/v3 v3.49.0
- golang.org/x/sync v0.3.0
+ github.com/ydb-platform/ydb-go-sdk/v3 v3.58.0
+ go.opentelemetry.io/otel v1.21.0
+ golang.org/x/sync v0.6.0
golang.org/x/time v0.3.0
gorm.io/gorm v1.25.1
xorm.io/xorm v1.3.2
@@ -15,16 +16,19 @@ require (
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/go-logr/logr v1.4.1 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
- github.com/google/uuid v1.3.0 // indirect
+ github.com/google/uuid v1.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
+ github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -37,18 +41,20 @@ require (
github.com/ydb-platform/ydb-go-sdk-auth-environ v0.2.0 // indirect
github.com/ydb-platform/ydb-go-yc v0.10.2 // indirect
github.com/ydb-platform/ydb-go-yc-metadata v0.5.3 // indirect
- golang.org/x/net v0.15.0 // indirect
- golang.org/x/sys v0.12.0 // indirect
- golang.org/x/text v0.13.0 // indirect
- google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
- google.golang.org/grpc v1.57.1 // indirect
- google.golang.org/protobuf v1.31.0 // indirect
+ go.opentelemetry.io/otel/metric v1.21.0 // indirect
+ go.opentelemetry.io/otel/trace v1.21.0 // indirect
+ golang.org/x/net v0.20.0 // indirect
+ golang.org/x/sys v0.16.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
+ google.golang.org/grpc v1.60.1 // indirect
+ google.golang.org/protobuf v1.33.0 // indirect
modernc.org/sqlite v1.24.0 // indirect
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
)
replace github.com/ydb-platform/ydb-go-sdk/v3 => ../../.
-replace xorm.io/xorm => github.com/ydb-platform/xorm v0.0.6
+replace xorm.io/xorm => github.com/ydb-platform/xorm v0.0.3
diff --git a/tests/slo/go.sum b/tests/slo/go.sum
index 41160165a..78bb98723 100644
--- a/tests/slo/go.sum
+++ b/tests/slo/go.sum
@@ -598,14 +598,16 @@ git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3p
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
-github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
@@ -619,13 +621,25 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
@@ -635,11 +649,9 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -655,18 +667,31 @@ github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
-github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
+github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -682,8 +707,11 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
@@ -696,6 +724,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
@@ -704,16 +733,26 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
-github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
-github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@@ -721,11 +760,11 @@ github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
-github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -779,8 +818,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -802,11 +842,12 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
@@ -825,77 +866,119 @@ github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57Q
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
+github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
+github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
-github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
-github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
-github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
-github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
-github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
+github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
-github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
-github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
+github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
@@ -915,26 +998,44 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -942,56 +1043,93 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
-github.com/rekby/fixenv v0.3.2/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM=
+github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@@ -999,20 +1137,34 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1029,17 +1181,20 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yandex-cloud/go-genproto v0.0.0-20211115083454-9ca41db5ed9e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
github.com/yandex-cloud/go-genproto v0.0.0-20230403093326-123923969dc6 h1:BkuaOCK1nc1eHSb/jCMwM2JZR00lJ9xNvnHvsZQgbRc=
github.com/yandex-cloud/go-genproto v0.0.0-20230403093326-123923969dc6/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
github.com/ydb-platform/gorm-driver v0.1.1 h1:PkN+sGSJehOZn9jQIFEmAOfhE73FNDMq+uzsnf7LVAM=
github.com/ydb-platform/gorm-driver v0.1.1/go.mod h1:Zv368SD5tHqkblmaOG6r2KTvtSIzPuB5p8rBaE6wVmw=
-github.com/ydb-platform/xorm v0.0.6 h1:mlclMIXR7Obwho3cYIIgBoMlMZ+APJZ9gnJQICyVAYY=
-github.com/ydb-platform/xorm v0.0.6/go.mod h1:vLAI6Xqpa+48y9I9HJnjD6IDKp/GnATYbtDgWzQb88c=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20240125100710-96fd3a874780 h1:E8Z7Zy/fKKDN4bYE1GvXX3DSjp9j7bPJy8Nnpe4Hxqg=
-github.com/ydb-platform/ydb-go-genproto v0.0.0-20240125100710-96fd3a874780/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
+github.com/ydb-platform/xorm v0.0.3 h1:MXk42lANB6r/MMLg/XdJfyXJycGUDlCeLiMlLGDKVPw=
+github.com/ydb-platform/xorm v0.0.3/go.mod h1:hFsU7EUF0o3S+l5c0eyP2yPVjJ0d4gsFdqCsyazzwBc=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf h1:ckwNHVo4bv2tqNkgx3W3HANh3ta1j6TR5qw08J1A7Tw=
github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=
github.com/ydb-platform/ydb-go-sdk-auth-environ v0.2.0 h1:IG5bPd+Lqyc+zsw2kmxqfGLkaDHuAEnWX63/8RBBiA4=
@@ -1060,6 +1215,10 @@ github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1068,6 +1227,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
+go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
+go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
+go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
+go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
+go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
@@ -1075,8 +1240,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec h1:aB0WVMCyiVcqL1yMRLM4htiFlMvgdOml97GYnw9su5Q=
-go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
+go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
@@ -1085,23 +1250,25 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1166,8 +1333,12 @@ golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1204,10 +1375,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -1229,8 +1398,9 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1276,12 +1446,17 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1294,9 +1469,11 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1318,6 +1495,7 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1341,6 +1519,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1351,7 +1530,6 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1372,8 +1550,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1402,8 +1581,10 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1411,7 +1592,9 @@ golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1419,6 +1602,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -1497,6 +1681,7 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6d
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1555,6 +1740,7 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -1566,6 +1752,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -1682,6 +1869,7 @@ google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ
google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
+google.golang.org/genproto v0.0.0-20230131230820-1c016267d619/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA=
google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
@@ -1693,18 +1881,26 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY=
-google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M=
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
-google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
+google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 h1:OPXtXn7fNMaXwO3JvOmF1QyTc00jsSFFz1vXXBOdCDo=
+google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1741,8 +1937,9 @@ google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsA
google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
-google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg=
google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
+google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
+google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1761,35 +1958,39 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1801,70 +2002,150 @@ honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
-modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
-modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
-modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
-modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0=
+modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
+modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
+modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
+modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
+modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
+modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
+modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
+modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
+modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
+modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
+modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
+modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
+modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
+modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
+modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
+modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
+modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
+modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
+modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
+modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
+modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
+modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
+modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
+modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
+modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
+modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
+modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
+modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
+modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
+modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4=
+modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
+modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
+modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
+modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
+modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws=
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
-modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
+modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
+modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
+modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
+modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
+modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
+modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
+modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
+modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
+modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
+modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
+modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
+modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
+modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
+modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
+modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
+modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
+modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
+modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
+modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
+modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
+modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
+modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
+modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
+modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
+modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
+modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
+modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
+modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
+modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
+modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
+modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
+modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
+modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
+modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s=
-modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
-modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
-modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
-modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
-modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
-modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
+modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
+modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
+modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
-modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
-modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
+modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
-modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A=
modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI=
modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
+modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
-modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
-modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
diff --git a/tests/slo/gorm/storage.go b/tests/slo/gorm/storage.go
index 2481014f1..b2cf92211 100644
--- a/tests/slo/gorm/storage.go
+++ b/tests/slo/gorm/storage.go
@@ -110,11 +110,9 @@ func (s *Storage) Read(ctx context.Context, id generator.RowID) (r generator.Row
retry.WithIdempotent(true),
retry.WithTrace(
&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopDoneInfo) {
- attempts = info.Attempts
- }
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
}
},
},
@@ -158,11 +156,9 @@ func (s *Storage) Write(ctx context.Context, row generator.Row) (attempts int, e
retry.WithIdempotent(true),
retry.WithTrace(
&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopDoneInfo) {
- attempts = info.Attempts
- }
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
}
},
},
diff --git a/tests/slo/internal/generator/row.go b/tests/slo/internal/generator/row.go
index 7a2d6aa2b..2eb2c1fc7 100644
--- a/tests/slo/internal/generator/row.go
+++ b/tests/slo/internal/generator/row.go
@@ -4,11 +4,12 @@ import "time"
type RowID = uint64
+//nolint:tagalign
type Row struct {
- Hash uint64 `gorm:"column:hash;primarykey;autoIncrement:false" xorm:"pk 'hash'"`
- ID RowID `gorm:"column:id;primarykey;autoIncrement:false" xorm:"pk 'id'"` //nolint:tagalign
- PayloadStr *string `gorm:"column:payload_str" xorm:"'payload_str'"` //nolint:tagalign
- PayloadDouble *float64 `gorm:"column:payload_double" xorm:"'payload_double'"` //nolint:tagalign
- PayloadTimestamp *time.Time `gorm:"column:payload_timestamp" xorm:"'payload_timestamp'"` //nolint:tagalign
- PayloadHash uint64 `gorm:"column:payload_hash" xorm:"'payload_hash'"` //nolint:tagalign
+ Hash uint64 `sql:"hash" gorm:"column:hash;primarykey;autoIncrement:false" xorm:"pk 'hash'"`
+ ID RowID `sql:"id" gorm:"column:id;primarykey;autoIncrement:false" xorm:"pk 'id'"`
+ PayloadStr *string `sql:"payload_str" gorm:"column:payload_str" xorm:"'payload_str'"`
+ PayloadDouble *float64 `sql:"payload_double" gorm:"column:payload_double" xorm:"'payload_double'"`
+ PayloadTimestamp *time.Time `sql:"payload_timestamp" gorm:"column:payload_timestamp" xorm:"'payload_timestamp'"`
+ PayloadHash uint64 `sql:"payload_hash" gorm:"column:payload_hash" xorm:"'payload_hash'"`
}
diff --git a/tests/slo/native/query/main.go b/tests/slo/native/query/main.go
new file mode 100644
index 000000000..138187877
--- /dev/null
+++ b/tests/slo/native/query/main.go
@@ -0,0 +1,139 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+
+ "golang.org/x/sync/errgroup"
+ "golang.org/x/time/rate"
+
+ "slo/internal/config"
+ "slo/internal/generator"
+ "slo/internal/workers"
+)
+
+var (
+ label string
+ jobName string
+)
+
+func main() {
+ ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
+ defer cancel()
+
+ cfg, err := config.New()
+ if err != nil {
+ panic(fmt.Errorf("create config failed: %w", err))
+ }
+
+ fmt.Println("program started")
+ defer fmt.Println("program finished")
+
+ ctx, cancel = context.WithTimeout(ctx, time.Duration(cfg.Time)*time.Second)
+ defer cancel()
+
+ s, err := NewStorage(ctx, cfg, cfg.ReadRPS+cfg.WriteRPS)
+ if err != nil {
+ panic(fmt.Errorf("create storage failed: %w", err))
+ }
+ defer func() {
+ var (
+ shutdownCtx context.Context
+ shutdownCancel context.CancelFunc
+ )
+ if cfg.ShutdownTime > 0 {
+ shutdownCtx, shutdownCancel = context.WithTimeout(context.Background(),
+ time.Duration(cfg.ShutdownTime)*time.Second)
+ } else {
+ shutdownCtx, shutdownCancel = context.WithCancel(context.Background())
+ }
+ defer shutdownCancel()
+
+ _ = s.close(shutdownCtx)
+ }()
+
+ fmt.Println("db init ok")
+
+ switch cfg.Mode {
+ case config.CreateMode:
+ err = s.createTable(ctx)
+ if err != nil {
+ panic(fmt.Errorf("create table failed: %w", err))
+ }
+ fmt.Println("create table ok")
+
+ gen := generator.New(0)
+
+ g := errgroup.Group{}
+
+ for i := uint64(0); i < cfg.InitialDataCount; i++ {
+ g.Go(func() (err error) {
+ e, err := gen.Generate()
+ if err != nil {
+ return err
+ }
+
+ _, err = s.Write(ctx, e)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+ }
+
+ err = g.Wait()
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Println("entries write ok")
+ case config.CleanupMode:
+ err = s.dropTable(ctx)
+ if err != nil {
+ panic(fmt.Errorf("create table failed: %w", err))
+ }
+
+ fmt.Println("cleanup table ok")
+ case config.RunMode:
+ gen := generator.New(cfg.InitialDataCount)
+
+ w, err := workers.New(cfg, s, label, jobName)
+ if err != nil {
+ panic(fmt.Errorf("create workers failed: %w", err))
+ }
+ defer func() {
+ err := w.Close()
+ if err != nil {
+ panic(fmt.Errorf("workers close failed: %w", err))
+ }
+ fmt.Println("workers close ok")
+ }()
+
+ wg := sync.WaitGroup{}
+
+ readRL := rate.NewLimiter(rate.Limit(cfg.ReadRPS), 1)
+ wg.Add(cfg.ReadRPS)
+ for i := 0; i < cfg.ReadRPS; i++ {
+ go w.Read(ctx, &wg, readRL)
+ }
+
+ writeRL := rate.NewLimiter(rate.Limit(cfg.WriteRPS), 1)
+ wg.Add(cfg.WriteRPS)
+ for i := 0; i < cfg.WriteRPS; i++ {
+ go w.Write(ctx, &wg, writeRL, gen)
+ }
+
+ metricsRL := rate.NewLimiter(rate.Every(time.Duration(cfg.ReportPeriod)*time.Millisecond), 1)
+ wg.Add(1)
+ go w.Metrics(ctx, &wg, metricsRL)
+
+ wg.Wait()
+ default:
+ panic(fmt.Errorf("unknown mode: %v", cfg.Mode))
+ }
+}
diff --git a/tests/slo/native/query/storage.go b/tests/slo/native/query/storage.go
new file mode 100755
index 000000000..89377d012
--- /dev/null
+++ b/tests/slo/native/query/storage.go
@@ -0,0 +1,269 @@
+package main
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "time"
+
+ ydb "github.com/ydb-platform/ydb-go-sdk/v3"
+ "github.com/ydb-platform/ydb-go-sdk/v3/log"
+ "github.com/ydb-platform/ydb-go-sdk/v3/query"
+ "github.com/ydb-platform/ydb-go-sdk/v3/trace"
+
+ "slo/internal/config"
+ "slo/internal/generator"
+)
+
+type Storage struct {
+ db *ydb.Driver
+ cfg *config.Config
+ tablePath string
+}
+
+const writeQuery = `
+DECLARE $id AS Uint64;
+DECLARE $payload_str AS Utf8;
+DECLARE $payload_double AS Double;
+DECLARE $payload_timestamp AS Timestamp;
+
+UPSERT INTO %s (
+ id, hash, payload_str, payload_double, payload_timestamp
+) VALUES (
+ $id, Digest::NumericHash($id), $payload_str, $payload_double, $payload_timestamp
+);
+`
+
+const readQuery = `
+DECLARE $id AS Uint64;
+SELECT id, payload_str, payload_double, payload_timestamp, payload_hash
+FROM %s WHERE id = $id AND hash = Digest::NumericHash($id);
+`
+
+const createTableQuery = `
+CREATE TABLE IF NOT EXISTS %s (
+ hash Uint64?,
+ id Uint64?,
+ payload_str Text?,
+ payload_double Double?,
+ payload_timestamp Timestamp?,
+ payload_hash Uint64?,
+ PRIMARY KEY (hash, id)
+) WITH (
+ UNIFORM_PARTITIONS = %d,
+ AUTO_PARTITIONING_BY_SIZE = ENABLED,
+ AUTO_PARTITIONING_PARTITION_SIZE_MB = %d,
+ AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = %d,
+ AUTO_PARTITIONING_MAX_PARTITIONS_COUNT = %d
+)
+`
+
+const dropTableQuery = `
+DROP TABLE %s
+`
+
+func NewStorage(ctx context.Context, cfg *config.Config, poolSize int) (*Storage, error) {
+ ctx, cancel := context.WithTimeout(ctx, time.Minute*5)
+ defer cancel()
+
+ db, err := ydb.Open(ctx,
+ cfg.Endpoint+cfg.DB,
+ ydb.WithSessionPoolSizeLimit(poolSize),
+ ydb.WithLogger(log.Default(os.Stderr, log.WithMinLevel(log.ERROR)), trace.DetailsAll),
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ prefix := path.Join(db.Name(), label)
+
+ s := &Storage{
+ db: db,
+ cfg: cfg,
+ tablePath: "`" + path.Join(prefix, cfg.Table) + "`",
+ }
+
+ return s, nil
+}
+
+func (s *Storage) Read(ctx context.Context, entryID generator.RowID) (_ generator.Row, attempts int, finalErr error) {
+ if err := ctx.Err(); err != nil {
+ return generator.Row{}, attempts, err
+ }
+
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(s.cfg.ReadTimeout)*time.Millisecond)
+ defer cancel()
+
+ e := generator.Row{}
+
+ err := s.db.Query().Do(ctx,
+ func(ctx context.Context, session query.Session) (err error) {
+ if err = ctx.Err(); err != nil {
+ return err
+ }
+
+ _, res, err := session.Execute(ctx,
+ fmt.Sprintf(readQuery, s.tablePath),
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$id").Uint64(entryID).
+ Build(),
+ ),
+ query.WithTxControl(query.TxControl(
+ query.BeginTx(query.WithOnlineReadOnly()),
+ query.CommitTx(),
+ )),
+ )
+ if err != nil {
+ return err
+ }
+ defer func() {
+ _ = res.Close(ctx)
+ }()
+
+ rs, err := res.NextResultSet(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ return nil
+ }
+
+ return err
+ }
+
+ row, err := rs.NextRow(ctx)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ return nil
+ }
+
+ return err
+ }
+
+ err = row.ScanStruct(&e, query.WithScanStructAllowMissingColumnsFromSelect())
+ if err != nil {
+ return err
+ }
+
+ return res.Err()
+ },
+ query.WithIdempotent(),
+ query.WithTrace(&trace.Query{
+ OnDo: func(info trace.QueryDoStartInfo) func(trace.QueryDoDoneInfo) {
+ return func(info trace.QueryDoDoneInfo) {
+ attempts = info.Attempts
+ }
+ },
+ }),
+ query.WithLabel("READ"),
+ )
+
+ return e, attempts, err
+}
+
+func (s *Storage) Write(ctx context.Context, e generator.Row) (attempts int, finalErr error) {
+ if err := ctx.Err(); err != nil {
+ return attempts, err
+ }
+
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(s.cfg.WriteTimeout)*time.Millisecond)
+ defer cancel()
+
+ err := s.db.Query().Do(ctx,
+ func(ctx context.Context, session query.Session) (err error) {
+ if err = ctx.Err(); err != nil {
+ return err
+ }
+
+ _, res, err := session.Execute(ctx,
+ fmt.Sprintf(writeQuery, s.tablePath),
+ query.WithParameters(
+ ydb.ParamsBuilder().
+ Param("$id").Uint64(e.ID).
+ Param("$payload_str").Text(*e.PayloadStr).
+ Param("$payload_double").Double(*e.PayloadDouble).
+ Param("$payload_timestamp").Timestamp(*e.PayloadTimestamp).
+ Build(),
+ ),
+ )
+ if err != nil {
+ return err
+ }
+
+ defer func() {
+ _ = res.Close(ctx)
+ }()
+
+ return res.Err()
+ },
+ query.WithIdempotent(),
+ query.WithTrace(&trace.Query{
+ OnDo: func(info trace.QueryDoStartInfo) func(trace.QueryDoDoneInfo) {
+ return func(info trace.QueryDoDoneInfo) {
+ attempts = info.Attempts
+ }
+ },
+ }),
+ query.WithLabel("WRITE"),
+ )
+
+ return attempts, err
+}
+
+func (s *Storage) createTable(ctx context.Context) error {
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(s.cfg.WriteTimeout)*time.Millisecond)
+ defer cancel()
+
+ return s.db.Query().Do(ctx,
+ func(ctx context.Context, session query.Session) error {
+ _, _, err := session.Execute(ctx,
+ fmt.Sprintf(createTableQuery, s.tablePath, s.cfg.MinPartitionsCount, s.cfg.PartitionSize,
+ s.cfg.MinPartitionsCount, s.cfg.MaxPartitionsCount,
+ ),
+ query.WithTxControl(query.NoTx()))
+
+ return err
+ }, query.WithIdempotent(),
+ query.WithLabel("CREATE TABLE"),
+ )
+}
+
+func (s *Storage) dropTable(ctx context.Context) error {
+ err := ctx.Err()
+ if err != nil {
+ return err
+ }
+
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(s.cfg.WriteTimeout)*time.Millisecond)
+ defer cancel()
+
+ return s.db.Query().Do(ctx,
+ func(ctx context.Context, session query.Session) error {
+ _, _, err := session.Execute(ctx,
+ fmt.Sprintf(dropTableQuery, s.tablePath),
+ query.WithTxControl(query.NoTx()),
+ )
+
+ return err
+ },
+ query.WithIdempotent(),
+ query.WithLabel("DROP TABLE"),
+ )
+}
+
+func (s *Storage) close(ctx context.Context) error {
+ var (
+ shutdownCtx context.Context
+ shutdownCancel context.CancelFunc
+ )
+ if s.cfg.ShutdownTime > 0 {
+ shutdownCtx, shutdownCancel = context.WithTimeout(ctx, time.Duration(s.cfg.ShutdownTime)*time.Second)
+ } else {
+ shutdownCtx, shutdownCancel = context.WithCancel(ctx)
+ }
+ defer shutdownCancel()
+
+ return s.db.Close(shutdownCtx)
+}
diff --git a/tests/slo/native/main.go b/tests/slo/native/table/main.go
similarity index 100%
rename from tests/slo/native/main.go
rename to tests/slo/native/table/main.go
diff --git a/tests/slo/native/storage.go b/tests/slo/native/table/storage.go
similarity index 91%
rename from tests/slo/native/storage.go
rename to tests/slo/native/table/storage.go
index 0024266a8..220054aa0 100755
--- a/tests/slo/native/storage.go
+++ b/tests/slo/native/table/storage.go
@@ -142,11 +142,9 @@ func (s *Storage) Read(ctx context.Context, entryID generator.RowID) (_ generato
},
table.WithIdempotent(),
table.WithTrace(trace.Table{
- OnDo: func(info trace.TableDoStartInfo) func(info trace.TableDoIntermediateInfo) func(trace.TableDoDoneInfo) {
- return func(info trace.TableDoIntermediateInfo) func(trace.TableDoDoneInfo) {
- return func(info trace.TableDoDoneInfo) {
- attempts = info.Attempts
- }
+ OnDo: func(info trace.TableDoStartInfo) func(trace.TableDoDoneInfo) {
+ return func(info trace.TableDoDoneInfo) {
+ attempts = info.Attempts
}
},
}),
@@ -190,11 +188,9 @@ func (s *Storage) Write(ctx context.Context, e generator.Row) (attempts int, _ e
},
table.WithIdempotent(),
table.WithTrace(trace.Table{
- OnDo: func(info trace.TableDoStartInfo) func(info trace.TableDoIntermediateInfo) func(trace.TableDoDoneInfo) {
- return func(info trace.TableDoIntermediateInfo) func(trace.TableDoDoneInfo) {
- return func(info trace.TableDoDoneInfo) {
- attempts = info.Attempts
- }
+ OnDo: func(info trace.TableDoStartInfo) func(trace.TableDoDoneInfo) {
+ return func(info trace.TableDoDoneInfo) {
+ attempts = info.Attempts
}
},
}),
diff --git a/tests/slo/xorm/storage.go b/tests/slo/xorm/storage.go
index 87409a738..4550374c9 100644
--- a/tests/slo/xorm/storage.go
+++ b/tests/slo/xorm/storage.go
@@ -141,11 +141,9 @@ func (s *Storage) Read(ctx context.Context, id generator.RowID) (row generator.R
retry.WithIdempotent(true),
retry.WithTrace(
&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopDoneInfo) {
- attempts = info.Attempts
- }
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
}
},
},
@@ -176,11 +174,9 @@ func (s *Storage) Write(ctx context.Context, row generator.Row) (attempts int, e
retry.WithIdempotent(true),
retry.WithTrace(
&trace.Retry{
- OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) {
- return func(info trace.RetryLoopDoneInfo) {
- attempts = info.Attempts
- }
+ OnRetry: func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) {
+ return func(info trace.RetryLoopDoneInfo) {
+ attempts = info.Attempts
}
},
},
diff --git a/testutil/compare.go b/testutil/compare.go
index 13d3e7369..8caf4b9bf 100644
--- a/testutil/compare.go
+++ b/testutil/compare.go
@@ -34,10 +34,10 @@ func Compare(l, r value.Value) (int, error) {
}
func unwrapTypedValue(v *Ydb.TypedValue) *Ydb.TypedValue {
- typ := v.Type
- val := v.Value
+ typ := v.GetType()
+ val := v.GetValue()
for opt := typ.GetOptionalType(); opt != nil; opt = typ.GetOptionalType() {
- typ = opt.Item
+ typ = opt.GetItem()
if nested := val.GetNestedValue(); nested != nil {
val = nested
}
@@ -47,18 +47,18 @@ func unwrapTypedValue(v *Ydb.TypedValue) *Ydb.TypedValue {
}
func compare(lhs, rhs *Ydb.TypedValue) (int, error) {
- lTypeID := lhs.Type.GetTypeId()
- rTypeID := rhs.Type.GetTypeId()
+ lTypeID := lhs.GetType().GetTypeId()
+ rTypeID := rhs.GetType().GetTypeId()
switch {
case lTypeID != rTypeID:
return 0, notComparableError(lhs, rhs)
case lTypeID != Ydb.Type_PRIMITIVE_TYPE_ID_UNSPECIFIED:
- return comparePrimitives(lTypeID, lhs.Value, rhs.Value)
- case lhs.Type.GetTupleType() != nil && rhs.Type.GetTupleType() != nil:
+ return comparePrimitives(lTypeID, lhs.GetValue(), rhs.GetValue())
+ case lhs.GetType().GetTupleType() != nil && rhs.GetType().GetTupleType() != nil:
return compareTuplesOrLists(expandTuple(lhs), expandTuple(rhs))
- case lhs.Type.GetListType() != nil && rhs.Type.GetListType() != nil:
+ case lhs.GetType().GetListType() != nil && rhs.GetType().GetListType() != nil:
return compareTuplesOrLists(expandList(lhs), expandList(rhs))
- case lhs.Type.GetStructType() != nil && rhs.Type.GetStructType() != nil:
+ case lhs.GetType().GetStructType() != nil && rhs.GetType().GetStructType() != nil:
return compareStructs(expandStruct(lhs), expandStruct(rhs))
default:
return 0, notComparableError(lhs, rhs)
@@ -88,11 +88,11 @@ func expandStruct(v *Ydb.TypedValue) []*Ydb.TypedValue {
}
func expandTuple(v *Ydb.TypedValue) []*Ydb.TypedValue {
- tuple := v.Type.GetTupleType()
- size := len(tuple.Elements)
+ tuple := v.GetType().GetTupleType()
+ size := len(tuple.GetElements())
values := make([]*Ydb.TypedValue, 0, size)
- for idx, typ := range tuple.Elements {
- values = append(values, unwrapTypedValue(&Ydb.TypedValue{Type: typ, Value: v.Value.Items[idx]}))
+ for idx, typ := range tuple.GetElements() {
+ values = append(values, unwrapTypedValue(&Ydb.TypedValue{Type: typ, Value: v.GetValue().GetItems()[idx]}))
}
return values
@@ -103,8 +103,8 @@ func notComparableError(lhs, rhs interface{}) error {
}
func comparePrimitives(t Ydb.Type_PrimitiveTypeId, lhs, rhs *Ydb.Value) (int, error) {
- _, lIsNull := lhs.Value.(*Ydb.Value_NullFlagValue)
- _, rIsNull := rhs.Value.(*Ydb.Value_NullFlagValue)
+ _, lIsNull := lhs.GetValue().(*Ydb.Value_NullFlagValue)
+ _, rIsNull := rhs.GetValue().(*Ydb.Value_NullFlagValue)
if lIsNull {
if rIsNull {
return 0, nil
diff --git a/testutil/compare_test.go b/testutil/compare_test.go
index 5041b1b74..45f2ed2c1 100644
--- a/testutil/compare_test.go
+++ b/testutil/compare_test.go
@@ -8,8 +8,8 @@ import (
"google.golang.org/protobuf/types/known/structpb"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator"
+ "github.com/ydb-platform/ydb-go-sdk/v3/internal/types"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
- "github.com/ydb-platform/ydb-go-sdk/v3/table/types"
)
func TestUnwrapOptionalValue(t *testing.T) {
@@ -17,11 +17,11 @@ func TestUnwrapOptionalValue(t *testing.T) {
defer a.Free()
v := value.OptionalValue(value.OptionalValue(value.TextValue("a")))
val := unwrapTypedValue(value.ToYDB(v, a))
- typeID := val.Type.GetTypeId()
+ typeID := val.GetType().GetTypeId()
if typeID != Ydb.Type_UTF8 {
t.Errorf("Types are different: expected %d, actual %d", Ydb.Type_UTF8, typeID)
}
- textValue := val.Value.Value.(*Ydb.Value_TextValue)
+ textValue := val.GetValue().GetValue().(*Ydb.Value_TextValue)
text := textValue.TextValue
if text != "a" {
t.Errorf("Values are different: expected %q, actual %q", "a", text)
@@ -33,11 +33,11 @@ func TestUnwrapPrimitiveValue(t *testing.T) {
defer a.Free()
v := value.TextValue("a")
val := unwrapTypedValue(value.ToYDB(v, a))
- typeID := val.Type.GetTypeId()
+ typeID := val.GetType().GetTypeId()
if typeID != Ydb.Type_UTF8 {
t.Errorf("Types are different: expected %d, actual %d", Ydb.Type_UTF8, typeID)
}
- textValue := val.Value.Value.(*Ydb.Value_TextValue)
+ textValue := val.GetValue().GetValue().(*Ydb.Value_TextValue)
text := textValue.TextValue
if text != "a" {
t.Errorf("Values are different: expected %q, actual %q", "a", text)
@@ -47,21 +47,21 @@ func TestUnwrapPrimitiveValue(t *testing.T) {
func TestUnwrapNullValue(t *testing.T) {
a := allocator.New()
defer a.Free()
- v := value.NullValue(value.TypeText)
+ v := value.NullValue(types.Text)
val := unwrapTypedValue(value.ToYDB(v, a))
- typeID := val.Type.GetTypeId()
+ typeID := val.GetType().GetTypeId()
if typeID != Ydb.Type_UTF8 {
t.Errorf("Types are different: expected %d, actual %d", Ydb.Type_UTF8, typeID)
}
- nullFlagValue := val.Value.Value.(*Ydb.Value_NullFlagValue)
+ nullFlagValue := val.GetValue().GetValue().(*Ydb.Value_NullFlagValue)
if nullFlagValue.NullFlagValue != structpb.NullValue_NULL_VALUE {
t.Errorf("Values are different: expected %d, actual %d", structpb.NullValue_NULL_VALUE, nullFlagValue.NullFlagValue)
}
}
func TestUint8(t *testing.T) {
- l := types.Uint8Value(byte(1))
- r := types.Uint8Value(byte(10))
+ l := value.Uint8Value(byte(1))
+ r := value.Uint8Value(byte(10))
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -76,8 +76,8 @@ func TestUint8(t *testing.T) {
}
func TestInt8(t *testing.T) {
- l := types.Int8Value(int8(1))
- r := types.Int8Value(int8(10))
+ l := value.Int8Value(int8(1))
+ r := value.Int8Value(int8(10))
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -92,8 +92,8 @@ func TestInt8(t *testing.T) {
}
func TestTimestamp(t *testing.T) {
- l := types.TimestampValue(1)
- r := types.TimestampValue(10)
+ l := value.TimestampValue(1)
+ r := value.TimestampValue(10)
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -108,8 +108,8 @@ func TestTimestamp(t *testing.T) {
}
func TestDateTime(t *testing.T) {
- l := types.DatetimeValue(1)
- r := types.DatetimeValue(10)
+ l := value.DatetimeValue(1)
+ r := value.DatetimeValue(10)
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -124,8 +124,8 @@ func TestDateTime(t *testing.T) {
}
func TestUint64(t *testing.T) {
- l := types.Uint64Value(uint64(1))
- r := types.Uint64Value(uint64(10))
+ l := value.Uint64Value(uint64(1))
+ r := value.Uint64Value(uint64(10))
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -140,8 +140,8 @@ func TestUint64(t *testing.T) {
}
func TestInt64(t *testing.T) {
- l := types.Int64Value(int64(1))
- r := types.Int64Value(int64(10))
+ l := value.Int64Value(int64(1))
+ r := value.Int64Value(int64(10))
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -156,8 +156,8 @@ func TestInt64(t *testing.T) {
}
func TestDouble(t *testing.T) {
- l := types.DoubleValue(1.0)
- r := types.DoubleValue(2.0)
+ l := value.DoubleValue(1.0)
+ r := value.DoubleValue(2.0)
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -172,8 +172,8 @@ func TestDouble(t *testing.T) {
}
func TestFloat(t *testing.T) {
- l := types.FloatValue(1.0)
- r := types.FloatValue(2.0)
+ l := value.FloatValue(1.0)
+ r := value.FloatValue(2.0)
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -188,8 +188,8 @@ func TestFloat(t *testing.T) {
}
func TestUTF8(t *testing.T) {
- l := types.TextValue("abc")
- r := types.TextValue("abx")
+ l := value.TextValue("abc")
+ r := value.TextValue("abx")
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -204,8 +204,8 @@ func TestUTF8(t *testing.T) {
}
func TestOptionalUTF8(t *testing.T) {
- l := types.OptionalValue(types.OptionalValue(types.TextValue("abc")))
- r := types.TextValue("abx")
+ l := value.OptionalValue(value.OptionalValue(value.TextValue("abc")))
+ r := value.TextValue("abx")
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -220,8 +220,8 @@ func TestOptionalUTF8(t *testing.T) {
}
func TestBytes(t *testing.T) {
- l := types.BytesValue([]byte{1, 2, 3})
- r := types.BytesValue([]byte{1, 2, 5})
+ l := value.BytesValue([]byte{1, 2, 3})
+ r := value.BytesValue([]byte{1, 2, 5})
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -236,8 +236,8 @@ func TestBytes(t *testing.T) {
}
func TestNull(t *testing.T) {
- l := types.NullValue(types.TypeText)
- r := types.TextValue("abc")
+ l := value.NullValue(types.Text)
+ r := value.TextValue("abc")
c, err := Compare(l, r)
requireNoError(t, err)
@@ -253,10 +253,10 @@ func TestNull(t *testing.T) {
}
func TestTuple(t *testing.T) {
- withNull := types.TupleValue(types.Uint64Value(1), types.NullValue(types.TypeText))
- least := types.TupleValue(types.Uint64Value(1), types.TextValue("abc"))
- medium := types.TupleValue(types.Uint64Value(1), types.TextValue("def"))
- largest := types.TupleValue(types.Uint64Value(2), types.TextValue("abc"))
+ withNull := value.TupleValue(value.Uint64Value(1), value.NullValue(types.Text))
+ least := value.TupleValue(value.Uint64Value(1), value.TextValue("abc"))
+ medium := value.TupleValue(value.Uint64Value(1), value.TextValue("def"))
+ largest := value.TupleValue(value.Uint64Value(2), value.TextValue("abc"))
c, err := Compare(least, medium)
requireNoError(t, err)
@@ -280,9 +280,9 @@ func TestTuple(t *testing.T) {
}
func TestList(t *testing.T) {
- least := types.ListValue(types.Uint64Value(1), types.Uint64Value(1))
- medium := types.ListValue(types.Uint64Value(1), types.Uint64Value(2))
- largest := types.ListValue(types.Uint64Value(2), types.Uint64Value(1))
+ least := value.ListValue(value.Uint64Value(1), value.Uint64Value(1))
+ medium := value.ListValue(value.Uint64Value(1), value.Uint64Value(2))
+ largest := value.ListValue(value.Uint64Value(2), value.Uint64Value(1))
c, err := Compare(least, medium)
requireNoError(t, err)
@@ -298,8 +298,8 @@ func TestList(t *testing.T) {
}
func TestDyNumber(t *testing.T) {
- l := types.DyNumberValue("2")
- r := types.DyNumberValue("12")
+ l := value.DyNumberValue("2")
+ r := value.DyNumberValue("12")
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -314,9 +314,9 @@ func TestDyNumber(t *testing.T) {
}
func TestUUID(t *testing.T) {
- l := types.UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})
- r := types.UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17})
- g := types.UUIDValue([16]byte{100, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17})
+ l := value.UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})
+ r := value.UUIDValue([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17})
+ g := value.UUIDValue([16]byte{100, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17})
c, err := Compare(l, r)
requireNoError(t, err)
requireEqualValues(t, -1, c)
@@ -335,8 +335,8 @@ func TestUUID(t *testing.T) {
}
func TestIncompatiblePrimitives(t *testing.T) {
- l := types.Uint64Value(1)
- r := types.TimestampValue(2)
+ l := value.Uint64Value(1)
+ r := value.TimestampValue(2)
_, err := Compare(l, r)
if err == nil {
t.Errorf("WithStackTrace expected")
@@ -347,8 +347,8 @@ func TestIncompatiblePrimitives(t *testing.T) {
}
func TestIncompatibleTuples(t *testing.T) {
- l := types.TupleValue(types.Uint64Value(1), types.TextValue("abc"))
- r := types.TupleValue(types.Uint64Value(1), types.BytesValue([]byte("abc")))
+ l := value.TupleValue(value.Uint64Value(1), value.TextValue("abc"))
+ r := value.TupleValue(value.Uint64Value(1), value.BytesValue([]byte("abc")))
_, err := Compare(l, r)
if err == nil {
t.Error("WithStackTrace expected")
@@ -358,8 +358,8 @@ func TestIncompatibleTuples(t *testing.T) {
}
func TestTupleOfDifferentLength(t *testing.T) {
- l := types.TupleValue(types.Uint64Value(1), types.TextValue("abc"))
- r := types.TupleValue(types.Uint64Value(1), types.TextValue("abc"), types.TextValue("def"))
+ l := value.TupleValue(value.Uint64Value(1), value.TextValue("abc"))
+ r := value.TupleValue(value.Uint64Value(1), value.TextValue("abc"), value.TextValue("def"))
cmp, err := Compare(l, r)
requireNoError(t, err)
@@ -371,8 +371,8 @@ func TestTupleOfDifferentLength(t *testing.T) {
}
func TestTupleInTuple(t *testing.T) {
- l := types.TupleValue(types.Uint64Value(1), types.TupleValue(types.TextValue("abc"), types.BytesValue([]byte("xyz"))))
- r := types.TupleValue(types.Uint64Value(1), types.TupleValue(types.TextValue("def"), types.BytesValue([]byte("xyz"))))
+ l := value.TupleValue(value.Uint64Value(1), value.TupleValue(value.TextValue("abc"), value.BytesValue([]byte("xyz"))))
+ r := value.TupleValue(value.Uint64Value(1), value.TupleValue(value.TextValue("def"), value.BytesValue([]byte("xyz"))))
cmp, err := Compare(l, r)
requireNoError(t, err)
@@ -388,18 +388,18 @@ func TestTupleInTuple(t *testing.T) {
}
func TestListInList(t *testing.T) {
- l := types.ListValue(
- types.ListValue(
- types.TextValue("abc"), types.TextValue("def"),
- ), types.ListValue(
- types.TextValue("uvw"), types.TextValue("xyz"),
+ l := value.ListValue(
+ value.ListValue(
+ value.TextValue("abc"), value.TextValue("def"),
+ ), value.ListValue(
+ value.TextValue("uvw"), value.TextValue("xyz"),
),
)
- r := types.ListValue(
- types.ListValue(
- types.TextValue("abc"), types.TextValue("deg"),
- ), types.ListValue(
- types.TextValue("uvw"), types.TextValue("xyz"),
+ r := value.ListValue(
+ value.ListValue(
+ value.TextValue("abc"), value.TextValue("deg"),
+ ), value.ListValue(
+ value.TextValue("uvw"), value.TextValue("xyz"),
),
)
diff --git a/testutil/session.go b/testutil/session.go
index e53caedf2..d5ca81899 100644
--- a/testutil/session.go
+++ b/testutil/session.go
@@ -35,9 +35,9 @@ func SessionID(opts ...sessionIDOption) string {
nodeID: uint32(xrand.New().Int64(math.MaxUint32)),
hash: strconv.FormatInt(xrand.New().Int64(math.MaxInt64), 16),
}
- for _, o := range opts {
- if o != nil {
- o(h)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(h)
}
}
diff --git a/topic/topicreader/errors.go b/topic/topicreader/errors.go
index 199a68257..f5f4db2d0 100644
--- a/topic/topicreader/errors.go
+++ b/topic/topicreader/errors.go
@@ -9,7 +9,7 @@ import (
// ErrUnexpectedCodec will return if topicreader receive message with unknown codec.
// client side must check error with errors.Is
-var ErrUnexpectedCodec = topicreaderinternal.PublicErrUnexpectedCodec
+var ErrUnexpectedCodec = topicreaderinternal.ErrPublicUnexpectedCodec
// ErrConcurrencyCall return if method on reader called in concurrency
// client side must check error with errors.Is
diff --git a/trace/coordination.go b/trace/coordination.go
index 551064671..8c9ac18a0 100644
--- a/trace/coordination.go
+++ b/trace/coordination.go
@@ -1,5 +1,12 @@
package trace
+import (
+ "context"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+)
+
// tool gtrace used from ./internal/cmd/gtrace
//go:generate gtrace
@@ -7,5 +14,168 @@ package trace
type (
// Coordination specified trace of coordination client activity.
// gtrace:gen
- Coordination struct{}
+ Coordination struct {
+ OnNew func(CoordinationNewStartInfo) func(CoordinationNewDoneInfo)
+ OnCreateNode func(CoordinationCreateNodeStartInfo) func(CoordinationCreateNodeDoneInfo)
+ OnAlterNode func(CoordinationAlterNodeStartInfo) func(CoordinationAlterNodeDoneInfo)
+ OnDropNode func(CoordinationDropNodeStartInfo) func(CoordinationDropNodeDoneInfo)
+ OnDescribeNode func(CoordinationDescribeNodeStartInfo) func(CoordinationDescribeNodeDoneInfo)
+ OnSession func(CoordinationSessionStartInfo) func(CoordinationSessionDoneInfo)
+ OnClose func(CoordinationCloseStartInfo) func(CoordinationCloseDoneInfo)
+
+ OnStreamNew func(CoordinationStreamNewStartInfo) func(CoordinationStreamNewDoneInfo)
+ OnSessionStarted func(CoordinationSessionStartedInfo)
+ OnSessionStartTimeout func(CoordinationSessionStartTimeoutInfo)
+ OnSessionKeepAliveTimeout func(CoordinationSessionKeepAliveTimeoutInfo)
+ OnSessionStopped func(CoordinationSessionStoppedInfo)
+ OnSessionStopTimeout func(CoordinationSessionStopTimeoutInfo)
+ OnSessionClientTimeout func(CoordinationSessionClientTimeoutInfo)
+ OnSessionServerExpire func(CoordinationSessionServerExpireInfo)
+ OnSessionServerError func(CoordinationSessionServerErrorInfo)
+
+ OnSessionReceive func(CoordinationSessionReceiveStartInfo) func(CoordinationSessionReceiveDoneInfo)
+ OnSessionReceiveUnexpected func(CoordinationSessionReceiveUnexpectedInfo)
+
+ OnSessionStop func(CoordinationSessionStopInfo)
+ OnSessionStart func(CoordinationSessionStartStartInfo) func(CoordinationSessionStartDoneInfo)
+ OnSessionSend func(CoordinationSessionSendStartInfo) func(CoordinationSessionSendDoneInfo)
+ }
+ CoordinationNewStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ CoordinationNewDoneInfo struct{}
+ CoordinationCloseStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ CoordinationCloseDoneInfo struct {
+ Error error
+ }
+ CoordinationCreateNodeStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Path string
+ }
+ CoordinationCreateNodeDoneInfo struct {
+ Error error
+ }
+ CoordinationAlterNodeStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Path string
+ }
+ CoordinationAlterNodeDoneInfo struct {
+ Error error
+ }
+ CoordinationDropNodeStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Path string
+ }
+ CoordinationDropNodeDoneInfo struct {
+ Error error
+ }
+ CoordinationDescribeNodeStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Path string
+ }
+ CoordinationDescribeNodeDoneInfo struct {
+ Error error
+ }
+ CoordinationSessionStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Path string
+ }
+ CoordinationSessionDoneInfo struct {
+ Error error
+ }
+ CoordinationStreamNewStartInfo struct{}
+ CoordinationStreamNewDoneInfo struct {
+ Error error
+ }
+ CoordinationSessionStartedInfo struct {
+ SessionID uint64
+ ExpectedSessionID uint64
+ }
+ CoordinationSessionStartTimeoutInfo struct {
+ Timeout time.Duration
+ }
+ CoordinationSessionKeepAliveTimeoutInfo struct {
+ LastGoodResponseTime time.Time
+ Timeout time.Duration
+ }
+ CoordinationSessionStoppedInfo struct {
+ SessionID uint64
+ ExpectedSessionID uint64
+ }
+ CoordinationSessionStopTimeoutInfo struct {
+ Timeout time.Duration
+ }
+ CoordinationSessionClientTimeoutInfo struct {
+ LastGoodResponseTime time.Time
+ Timeout time.Duration
+ }
+ CoordinationSessionServerExpireInfo struct {
+ Failure *Ydb_Coordination.SessionResponse_Failure
+ }
+ CoordinationSessionServerErrorInfo struct {
+ Failure *Ydb_Coordination.SessionResponse_Failure
+ }
+ CoordinationSessionReceiveStartInfo struct{}
+ CoordinationSessionReceiveDoneInfo struct {
+ Response *Ydb_Coordination.SessionResponse
+ Error error
+ }
+ CoordinationSessionReceiveUnexpectedInfo struct {
+ Response *Ydb_Coordination.SessionResponse
+ }
+ CoordinationSessionStartStartInfo struct{}
+ CoordinationSessionStartDoneInfo struct {
+ Error error
+ }
+ CoordinationSessionStopInfo struct {
+ SessionID uint64
+ }
+ CoordinationSessionSendStartInfo struct {
+ Request *Ydb_Coordination.SessionRequest
+ }
+ CoordinationSessionSendDoneInfo struct {
+ Error error
+ }
)
diff --git a/trace/coordination_gtrace.go b/trace/coordination_gtrace.go
index 609198059..91ccf3747 100644
--- a/trace/coordination_gtrace.go
+++ b/trace/coordination_gtrace.go
@@ -2,6 +2,13 @@
package trace
+import (
+ "context"
+ "time"
+
+ "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination"
+)
+
// coordinationComposeOptions is a holder of options
type coordinationComposeOptions struct {
panicCallback func(e interface{})
@@ -20,5 +27,994 @@ func WithCoordinationPanicCallback(cb func(e interface{})) CoordinationComposeOp
// Compose returns a new Coordination which has functional fields composed both from t and x.
func (t *Coordination) Compose(x *Coordination, opts ...CoordinationComposeOption) *Coordination {
var ret Coordination
+ options := coordinationComposeOptions{}
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&options)
+ }
+ }
+ {
+ h1 := t.OnNew
+ h2 := x.OnNew
+ ret.OnNew = func(c CoordinationNewStartInfo) func(CoordinationNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationNewDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnCreateNode
+ h2 := x.OnCreateNode
+ ret.OnCreateNode = func(c CoordinationCreateNodeStartInfo) func(CoordinationCreateNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationCreateNodeDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationCreateNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnAlterNode
+ h2 := x.OnAlterNode
+ ret.OnAlterNode = func(c CoordinationAlterNodeStartInfo) func(CoordinationAlterNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationAlterNodeDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationAlterNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnDropNode
+ h2 := x.OnDropNode
+ ret.OnDropNode = func(c CoordinationDropNodeStartInfo) func(CoordinationDropNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationDropNodeDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationDropNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnDescribeNode
+ h2 := x.OnDescribeNode
+ ret.OnDescribeNode = func(c CoordinationDescribeNodeStartInfo) func(CoordinationDescribeNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationDescribeNodeDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationDescribeNodeDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSession
+ h2 := x.OnSession
+ ret.OnSession = func(c CoordinationSessionStartInfo) func(CoordinationSessionDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationSessionDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationSessionDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnClose
+ h2 := x.OnClose
+ ret.OnClose = func(c CoordinationCloseStartInfo) func(CoordinationCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationCloseDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnStreamNew
+ h2 := x.OnStreamNew
+ ret.OnStreamNew = func(c CoordinationStreamNewStartInfo) func(CoordinationStreamNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationStreamNewDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationStreamNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionStarted
+ h2 := x.OnSessionStarted
+ ret.OnSessionStarted = func(c CoordinationSessionStartedInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionStartTimeout
+ h2 := x.OnSessionStartTimeout
+ ret.OnSessionStartTimeout = func(c CoordinationSessionStartTimeoutInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionKeepAliveTimeout
+ h2 := x.OnSessionKeepAliveTimeout
+ ret.OnSessionKeepAliveTimeout = func(c CoordinationSessionKeepAliveTimeoutInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionStopped
+ h2 := x.OnSessionStopped
+ ret.OnSessionStopped = func(c CoordinationSessionStoppedInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionStopTimeout
+ h2 := x.OnSessionStopTimeout
+ ret.OnSessionStopTimeout = func(c CoordinationSessionStopTimeoutInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionClientTimeout
+ h2 := x.OnSessionClientTimeout
+ ret.OnSessionClientTimeout = func(c CoordinationSessionClientTimeoutInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionServerExpire
+ h2 := x.OnSessionServerExpire
+ ret.OnSessionServerExpire = func(c CoordinationSessionServerExpireInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionServerError
+ h2 := x.OnSessionServerError
+ ret.OnSessionServerError = func(c CoordinationSessionServerErrorInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionReceive
+ h2 := x.OnSessionReceive
+ ret.OnSessionReceive = func(c CoordinationSessionReceiveStartInfo) func(CoordinationSessionReceiveDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationSessionReceiveDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationSessionReceiveDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionReceiveUnexpected
+ h2 := x.OnSessionReceiveUnexpected
+ ret.OnSessionReceiveUnexpected = func(c CoordinationSessionReceiveUnexpectedInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionStop
+ h2 := x.OnSessionStop
+ ret.OnSessionStop = func(c CoordinationSessionStopInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(c)
+ }
+ if h2 != nil {
+ h2(c)
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionStart
+ h2 := x.OnSessionStart
+ ret.OnSessionStart = func(c CoordinationSessionStartStartInfo) func(CoordinationSessionStartDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationSessionStartDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationSessionStartDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionSend
+ h2 := x.OnSessionSend
+ ret.OnSessionSend = func(c CoordinationSessionSendStartInfo) func(CoordinationSessionSendDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(CoordinationSessionSendDoneInfo)
+ if h1 != nil {
+ r = h1(c)
+ }
+ if h2 != nil {
+ r1 = h2(c)
+ }
+ return func(c CoordinationSessionSendDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(c)
+ }
+ if r1 != nil {
+ r1(c)
+ }
+ }
+ }
+ }
return &ret
}
+func (t *Coordination) onNew(c CoordinationNewStartInfo) func(CoordinationNewDoneInfo) {
+ fn := t.OnNew
+ if fn == nil {
+ return func(CoordinationNewDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationNewDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onCreateNode(c CoordinationCreateNodeStartInfo) func(CoordinationCreateNodeDoneInfo) {
+ fn := t.OnCreateNode
+ if fn == nil {
+ return func(CoordinationCreateNodeDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationCreateNodeDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onAlterNode(c CoordinationAlterNodeStartInfo) func(CoordinationAlterNodeDoneInfo) {
+ fn := t.OnAlterNode
+ if fn == nil {
+ return func(CoordinationAlterNodeDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationAlterNodeDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onDropNode(c CoordinationDropNodeStartInfo) func(CoordinationDropNodeDoneInfo) {
+ fn := t.OnDropNode
+ if fn == nil {
+ return func(CoordinationDropNodeDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationDropNodeDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onDescribeNode(c CoordinationDescribeNodeStartInfo) func(CoordinationDescribeNodeDoneInfo) {
+ fn := t.OnDescribeNode
+ if fn == nil {
+ return func(CoordinationDescribeNodeDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationDescribeNodeDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onSession(c CoordinationSessionStartInfo) func(CoordinationSessionDoneInfo) {
+ fn := t.OnSession
+ if fn == nil {
+ return func(CoordinationSessionDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationSessionDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onClose(c CoordinationCloseStartInfo) func(CoordinationCloseDoneInfo) {
+ fn := t.OnClose
+ if fn == nil {
+ return func(CoordinationCloseDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationCloseDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onStreamNew(c CoordinationStreamNewStartInfo) func(CoordinationStreamNewDoneInfo) {
+ fn := t.OnStreamNew
+ if fn == nil {
+ return func(CoordinationStreamNewDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationStreamNewDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onSessionStarted(c CoordinationSessionStartedInfo) {
+ fn := t.OnSessionStarted
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionStartTimeout(c CoordinationSessionStartTimeoutInfo) {
+ fn := t.OnSessionStartTimeout
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionKeepAliveTimeout(c CoordinationSessionKeepAliveTimeoutInfo) {
+ fn := t.OnSessionKeepAliveTimeout
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionStopped(c CoordinationSessionStoppedInfo) {
+ fn := t.OnSessionStopped
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionStopTimeout(c CoordinationSessionStopTimeoutInfo) {
+ fn := t.OnSessionStopTimeout
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionClientTimeout(c CoordinationSessionClientTimeoutInfo) {
+ fn := t.OnSessionClientTimeout
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionServerExpire(c CoordinationSessionServerExpireInfo) {
+ fn := t.OnSessionServerExpire
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionServerError(c CoordinationSessionServerErrorInfo) {
+ fn := t.OnSessionServerError
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionReceive(c CoordinationSessionReceiveStartInfo) func(CoordinationSessionReceiveDoneInfo) {
+ fn := t.OnSessionReceive
+ if fn == nil {
+ return func(CoordinationSessionReceiveDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationSessionReceiveDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onSessionReceiveUnexpected(c CoordinationSessionReceiveUnexpectedInfo) {
+ fn := t.OnSessionReceiveUnexpected
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionStop(c CoordinationSessionStopInfo) {
+ fn := t.OnSessionStop
+ if fn == nil {
+ return
+ }
+ fn(c)
+}
+func (t *Coordination) onSessionStart(c CoordinationSessionStartStartInfo) func(CoordinationSessionStartDoneInfo) {
+ fn := t.OnSessionStart
+ if fn == nil {
+ return func(CoordinationSessionStartDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationSessionStartDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Coordination) onSessionSend(c CoordinationSessionSendStartInfo) func(CoordinationSessionSendDoneInfo) {
+ fn := t.OnSessionSend
+ if fn == nil {
+ return func(CoordinationSessionSendDoneInfo) {
+ return
+ }
+ }
+ res := fn(c)
+ if res == nil {
+ return func(CoordinationSessionSendDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func CoordinationOnNew(t *Coordination, c *context.Context, call call) func() {
+ var p CoordinationNewStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onNew(p)
+ return func() {
+ var p CoordinationNewDoneInfo
+ res(p)
+ }
+}
+func CoordinationOnCreateNode(t *Coordination, c *context.Context, call call, path string) func(error) {
+ var p CoordinationCreateNodeStartInfo
+ p.Context = c
+ p.Call = call
+ p.Path = path
+ res := t.onCreateNode(p)
+ return func(e error) {
+ var p CoordinationCreateNodeDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnAlterNode(t *Coordination, c *context.Context, call call, path string) func(error) {
+ var p CoordinationAlterNodeStartInfo
+ p.Context = c
+ p.Call = call
+ p.Path = path
+ res := t.onAlterNode(p)
+ return func(e error) {
+ var p CoordinationAlterNodeDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnDropNode(t *Coordination, c *context.Context, call call, path string) func(error) {
+ var p CoordinationDropNodeStartInfo
+ p.Context = c
+ p.Call = call
+ p.Path = path
+ res := t.onDropNode(p)
+ return func(e error) {
+ var p CoordinationDropNodeDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnDescribeNode(t *Coordination, c *context.Context, call call, path string) func(error) {
+ var p CoordinationDescribeNodeStartInfo
+ p.Context = c
+ p.Call = call
+ p.Path = path
+ res := t.onDescribeNode(p)
+ return func(e error) {
+ var p CoordinationDescribeNodeDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnSession(t *Coordination, c *context.Context, call call, path string) func(error) {
+ var p CoordinationSessionStartInfo
+ p.Context = c
+ p.Call = call
+ p.Path = path
+ res := t.onSession(p)
+ return func(e error) {
+ var p CoordinationSessionDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnClose(t *Coordination, c *context.Context, call call) func(error) {
+ var p CoordinationCloseStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onClose(p)
+ return func(e error) {
+ var p CoordinationCloseDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnStreamNew(t *Coordination) func(error) {
+ var p CoordinationStreamNewStartInfo
+ res := t.onStreamNew(p)
+ return func(e error) {
+ var p CoordinationStreamNewDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnSessionStarted(t *Coordination, sessionID uint64, expectedSessionID uint64) {
+ var p CoordinationSessionStartedInfo
+ p.SessionID = sessionID
+ p.ExpectedSessionID = expectedSessionID
+ t.onSessionStarted(p)
+}
+func CoordinationOnSessionStartTimeout(t *Coordination, timeout time.Duration) {
+ var p CoordinationSessionStartTimeoutInfo
+ p.Timeout = timeout
+ t.onSessionStartTimeout(p)
+}
+func CoordinationOnSessionKeepAliveTimeout(t *Coordination, lastGoodResponseTime time.Time, timeout time.Duration) {
+ var p CoordinationSessionKeepAliveTimeoutInfo
+ p.LastGoodResponseTime = lastGoodResponseTime
+ p.Timeout = timeout
+ t.onSessionKeepAliveTimeout(p)
+}
+func CoordinationOnSessionStopped(t *Coordination, sessionID uint64, expectedSessionID uint64) {
+ var p CoordinationSessionStoppedInfo
+ p.SessionID = sessionID
+ p.ExpectedSessionID = expectedSessionID
+ t.onSessionStopped(p)
+}
+func CoordinationOnSessionStopTimeout(t *Coordination, timeout time.Duration) {
+ var p CoordinationSessionStopTimeoutInfo
+ p.Timeout = timeout
+ t.onSessionStopTimeout(p)
+}
+func CoordinationOnSessionClientTimeout(t *Coordination, lastGoodResponseTime time.Time, timeout time.Duration) {
+ var p CoordinationSessionClientTimeoutInfo
+ p.LastGoodResponseTime = lastGoodResponseTime
+ p.Timeout = timeout
+ t.onSessionClientTimeout(p)
+}
+func CoordinationOnSessionServerExpire(t *Coordination, failure *Ydb_Coordination.SessionResponse_Failure) {
+ var p CoordinationSessionServerExpireInfo
+ p.Failure = failure
+ t.onSessionServerExpire(p)
+}
+func CoordinationOnSessionServerError(t *Coordination, failure *Ydb_Coordination.SessionResponse_Failure) {
+ var p CoordinationSessionServerErrorInfo
+ p.Failure = failure
+ t.onSessionServerError(p)
+}
+func CoordinationOnSessionReceive(t *Coordination) func(response *Ydb_Coordination.SessionResponse, _ error) {
+ var p CoordinationSessionReceiveStartInfo
+ res := t.onSessionReceive(p)
+ return func(response *Ydb_Coordination.SessionResponse, e error) {
+ var p CoordinationSessionReceiveDoneInfo
+ p.Response = response
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnSessionReceiveUnexpected(t *Coordination, response *Ydb_Coordination.SessionResponse) {
+ var p CoordinationSessionReceiveUnexpectedInfo
+ p.Response = response
+ t.onSessionReceiveUnexpected(p)
+}
+func CoordinationOnSessionStop(t *Coordination, sessionID uint64) {
+ var p CoordinationSessionStopInfo
+ p.SessionID = sessionID
+ t.onSessionStop(p)
+}
+func CoordinationOnSessionStart(t *Coordination) func(error) {
+ var p CoordinationSessionStartStartInfo
+ res := t.onSessionStart(p)
+ return func(e error) {
+ var p CoordinationSessionStartDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func CoordinationOnSessionSend(t *Coordination, request *Ydb_Coordination.SessionRequest) func(error) {
+ var p CoordinationSessionSendStartInfo
+ p.Request = request
+ res := t.onSessionSend(p)
+ return func(e error) {
+ var p CoordinationSessionSendDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
diff --git a/trace/details.go b/trace/details.go
index d2a65ff89..124885135 100644
--- a/trace/details.go
+++ b/trace/details.go
@@ -44,6 +44,11 @@ const (
TablePoolSessionLifeCycleEvents
TablePoolAPIEvents
+ QuerySessionEvents
+ QueryResultEvents
+ QueryTransactionEvents
+ QueryPoolEvents
+
TopicControlPlaneEvents
TopicReaderCustomerEvents
@@ -73,9 +78,6 @@ const (
CoordinationEvents
- // Deprecated: has no effect now
- DriverClusterEvents
-
DriverEvents = DriverConnEvents |
DriverBalancerEvents |
DriverResolverEvents |
@@ -90,6 +92,11 @@ const (
TablePoolSessionLifeCycleEvents |
TablePoolAPIEvents
+ QueryEvents = QuerySessionEvents |
+ QueryPoolEvents |
+ QueryResultEvents |
+ QueryTransactionEvents
+
TablePoolEvents = TablePoolLifeCycleEvents |
TablePoolSessionLifeCycleEvents |
TablePoolAPIEvents
@@ -144,6 +151,12 @@ var (
TablePoolSessionLifeCycleEvents: "ydb.table.pool.session",
TablePoolAPIEvents: "ydb.table.pool.api",
+ QueryEvents: "ydb.query",
+ QueryPoolEvents: "ydb.query.pool",
+ QuerySessionEvents: "ydb.query.session",
+ QueryResultEvents: "ydb.query.result",
+ QueryTransactionEvents: "ydb.query.tx",
+
DatabaseSQLEvents: "ydb.database.sql",
DatabaseSQLConnectorEvents: "ydb.database.sql.connector",
DatabaseSQLConnEvents: "ydb.database.sql.conn",
@@ -191,9 +204,9 @@ func MatchDetails(pattern string, opts ...matchDetailsOption) (d Details) {
err error
)
- for _, o := range opts {
- if o != nil {
- o(h)
+ for _, opt := range opts {
+ if opt != nil {
+ opt(h)
}
}
if h.posixMatch {
diff --git a/trace/details_test.go b/trace/details_test.go
index 1202daaf4..362090f68 100644
--- a/trace/details_test.go
+++ b/trace/details_test.go
@@ -35,6 +35,10 @@ func TestDetailsMatch(t *testing.T) {
pattern: `^ydb\.table`,
details: TableEvents,
},
+ {
+ pattern: `^ydb\.query`,
+ details: QueryEvents,
+ },
{
pattern: `^ydb\.scripting$`,
details: ScriptingEvents,
@@ -63,6 +67,10 @@ func TestDetailsMatch(t *testing.T) {
pattern: `^ydb\.table\.(pool\.(session|api)|session).*$`,
details: TablePoolSessionLifeCycleEvents | TablePoolAPIEvents | TableSessionEvents,
},
+ {
+ pattern: `^ydb\.query\.(pool|session|tx|result).*$`,
+ details: QueryPoolEvents | QuerySessionEvents | QueryTransactionEvents | QueryResultEvents,
+ },
{
pattern: `^ydb\.((database.sql.tx)|driver.(balancer|conn)|(table\.pool)|retry)$`,
details: DriverBalancerEvents | DriverConnEvents | TablePoolLifeCycleEvents | DatabaseSQLTxEvents | RetryEvents,
diff --git a/trace/driver.go b/trace/driver.go
index 9e954874c..e33dac555 100644
--- a/trace/driver.go
+++ b/trace/driver.go
@@ -24,35 +24,21 @@ type (
OnPoolNew func(DriverConnPoolNewStartInfo) func(DriverConnPoolNewDoneInfo)
OnPoolRelease func(DriverConnPoolReleaseStartInfo) func(DriverConnPoolReleaseDoneInfo)
- // Deprecated: driver not notificate about this event
- OnNetRead func(DriverNetReadStartInfo) func(DriverNetReadDoneInfo)
- // Deprecated: driver not notificate about this event
- OnNetWrite func(DriverNetWriteStartInfo) func(DriverNetWriteDoneInfo)
- // Deprecated: driver not notificate about this event
- OnNetDial func(DriverNetDialStartInfo) func(DriverNetDialDoneInfo)
- // Deprecated: driver not notificate about this event
- OnNetClose func(DriverNetCloseStartInfo) func(DriverNetCloseDoneInfo)
-
// Resolver events
OnResolve func(DriverResolveStartInfo) func(DriverResolveDoneInfo)
// Conn events
- OnConnStateChange func(DriverConnStateChangeStartInfo) func(DriverConnStateChangeDoneInfo)
- OnConnInvoke func(DriverConnInvokeStartInfo) func(DriverConnInvokeDoneInfo)
- OnConnNewStream func(
- DriverConnNewStreamStartInfo,
- ) func(
- DriverConnNewStreamRecvInfo,
- ) func(
- DriverConnNewStreamDoneInfo,
- )
- // Deprecated: driver not notificate about this event
- OnConnTake func(DriverConnTakeStartInfo) func(DriverConnTakeDoneInfo)
- OnConnDial func(DriverConnDialStartInfo) func(DriverConnDialDoneInfo)
- OnConnPark func(DriverConnParkStartInfo) func(DriverConnParkDoneInfo)
- OnConnBan func(DriverConnBanStartInfo) func(DriverConnBanDoneInfo)
- OnConnAllow func(DriverConnAllowStartInfo) func(DriverConnAllowDoneInfo)
- OnConnClose func(DriverConnCloseStartInfo) func(DriverConnCloseDoneInfo)
+ OnConnStateChange func(DriverConnStateChangeStartInfo) func(DriverConnStateChangeDoneInfo)
+ OnConnInvoke func(DriverConnInvokeStartInfo) func(DriverConnInvokeDoneInfo)
+ OnConnNewStream func(DriverConnNewStreamStartInfo) func(DriverConnNewStreamDoneInfo)
+ OnConnStreamRecvMsg func(DriverConnStreamRecvMsgStartInfo) func(DriverConnStreamRecvMsgDoneInfo)
+ OnConnStreamSendMsg func(DriverConnStreamSendMsgStartInfo) func(DriverConnStreamSendMsgDoneInfo)
+ OnConnStreamCloseSend func(DriverConnStreamCloseSendStartInfo) func(DriverConnStreamCloseSendDoneInfo)
+ OnConnDial func(DriverConnDialStartInfo) func(DriverConnDialDoneInfo)
+ OnConnBan func(DriverConnBanStartInfo) func(DriverConnBanDoneInfo)
+ OnConnAllow func(DriverConnAllowStartInfo) func(DriverConnAllowDoneInfo)
+ OnConnPark func(DriverConnParkStartInfo) func(DriverConnParkDoneInfo)
+ OnConnClose func(DriverConnCloseStartInfo) func(DriverConnCloseDoneInfo)
// Repeater events
OnRepeaterWakeUp func(DriverRepeaterWakeUpStartInfo) func(DriverRepeaterWakeUpDoneInfo)
@@ -60,12 +46,6 @@ type (
// Balancer events
OnBalancerInit func(DriverBalancerInitStartInfo) func(DriverBalancerInitDoneInfo)
- // Deprecated: driver not notificate about this event
- OnBalancerDialEntrypoint func(
- DriverBalancerDialEntrypointStartInfo,
- ) func(
- DriverBalancerDialEntrypointDoneInfo,
- )
OnBalancerClose func(DriverBalancerCloseStartInfo) func(DriverBalancerCloseDoneInfo)
OnBalancerChooseEndpoint func(
DriverBalancerChooseEndpointStartInfo,
@@ -172,8 +152,6 @@ type (
Added []EndpointInfo
Dropped []EndpointInfo
LocalDC string
- // Deprecated: this field always nil
- Error error
}
DriverBalancerClusterDiscoveryAttemptStartInfo struct {
// Context make available context in trace callback function.
@@ -326,13 +304,42 @@ type (
Endpoint EndpointInfo
Method Method
}
- DriverConnNewStreamRecvInfo struct {
+ DriverConnNewStreamDoneInfo struct {
Error error
+ State ConnState
}
- DriverConnNewStreamDoneInfo struct {
- Error error
- State ConnState
- Metadata map[string][]string
+ DriverConnStreamRecvMsgStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ DriverConnStreamRecvMsgDoneInfo struct {
+ Error error
+ }
+ DriverConnStreamSendMsgStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ DriverConnStreamSendMsgDoneInfo struct {
+ Error error
+ }
+ DriverConnStreamCloseSendStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ DriverConnStreamCloseSendDoneInfo struct {
+ Error error
}
DriverBalancerInitStartInfo struct {
// Context make available context in trace callback function.
diff --git a/trace/driver_gtrace.go b/trace/driver_gtrace.go
index 0fe4e4fe7..4e737f5db 100644
--- a/trace/driver_gtrace.go
+++ b/trace/driver_gtrace.go
@@ -206,44 +206,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnNetRead
- h2 := x.OnNetRead
- ret.OnNetRead = func(d DriverNetReadStartInfo) func(DriverNetReadDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- var r, r1 func(DriverNetReadDoneInfo)
- if h1 != nil {
- r = h1(d)
- }
- if h2 != nil {
- r1 = h2(d)
- }
- return func(d DriverNetReadDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r != nil {
- r(d)
- }
- if r1 != nil {
- r1(d)
- }
- }
- }
- }
- {
- h1 := t.OnNetWrite
- h2 := x.OnNetWrite
- ret.OnNetWrite = func(d DriverNetWriteStartInfo) func(DriverNetWriteDoneInfo) {
+ h1 := t.OnResolve
+ h2 := x.OnResolve
+ ret.OnResolve = func(d DriverResolveStartInfo) func(DriverResolveDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -251,14 +216,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverNetWriteDoneInfo)
+ var r, r1 func(DriverResolveDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverNetWriteDoneInfo) {
+ return func(d DriverResolveDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -276,9 +241,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnNetDial
- h2 := x.OnNetDial
- ret.OnNetDial = func(d DriverNetDialStartInfo) func(DriverNetDialDoneInfo) {
+ h1 := t.OnConnStateChange
+ h2 := x.OnConnStateChange
+ ret.OnConnStateChange = func(d DriverConnStateChangeStartInfo) func(DriverConnStateChangeDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -286,14 +251,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverNetDialDoneInfo)
+ var r, r1 func(DriverConnStateChangeDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverNetDialDoneInfo) {
+ return func(d DriverConnStateChangeDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -311,9 +276,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnNetClose
- h2 := x.OnNetClose
- ret.OnNetClose = func(d DriverNetCloseStartInfo) func(DriverNetCloseDoneInfo) {
+ h1 := t.OnConnInvoke
+ h2 := x.OnConnInvoke
+ ret.OnConnInvoke = func(d DriverConnInvokeStartInfo) func(DriverConnInvokeDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -321,14 +286,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverNetCloseDoneInfo)
+ var r, r1 func(DriverConnInvokeDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverNetCloseDoneInfo) {
+ return func(d DriverConnInvokeDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -346,9 +311,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnResolve
- h2 := x.OnResolve
- ret.OnResolve = func(d DriverResolveStartInfo) func(DriverResolveDoneInfo) {
+ h1 := t.OnConnNewStream
+ h2 := x.OnConnNewStream
+ ret.OnConnNewStream = func(d DriverConnNewStreamStartInfo) func(DriverConnNewStreamDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -356,14 +321,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverResolveDoneInfo)
+ var r, r1 func(DriverConnNewStreamDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverResolveDoneInfo) {
+ return func(d DriverConnNewStreamDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -381,9 +346,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnConnStateChange
- h2 := x.OnConnStateChange
- ret.OnConnStateChange = func(d DriverConnStateChangeStartInfo) func(DriverConnStateChangeDoneInfo) {
+ h1 := t.OnConnStreamRecvMsg
+ h2 := x.OnConnStreamRecvMsg
+ ret.OnConnStreamRecvMsg = func(d DriverConnStreamRecvMsgStartInfo) func(DriverConnStreamRecvMsgDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -391,14 +356,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverConnStateChangeDoneInfo)
+ var r, r1 func(DriverConnStreamRecvMsgDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverConnStateChangeDoneInfo) {
+ return func(d DriverConnStreamRecvMsgDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -416,9 +381,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnConnInvoke
- h2 := x.OnConnInvoke
- ret.OnConnInvoke = func(d DriverConnInvokeStartInfo) func(DriverConnInvokeDoneInfo) {
+ h1 := t.OnConnStreamSendMsg
+ h2 := x.OnConnStreamSendMsg
+ ret.OnConnStreamSendMsg = func(d DriverConnStreamSendMsgStartInfo) func(DriverConnStreamSendMsgDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -426,14 +391,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverConnInvokeDoneInfo)
+ var r, r1 func(DriverConnStreamSendMsgDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverConnInvokeDoneInfo) {
+ return func(d DriverConnStreamSendMsgDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -451,60 +416,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnConnNewStream
- h2 := x.OnConnNewStream
- ret.OnConnNewStream = func(d DriverConnNewStreamStartInfo) func(DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- var r, r1 func(DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo)
- if h1 != nil {
- r = h1(d)
- }
- if h2 != nil {
- r1 = h2(d)
- }
- return func(d DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- var r2, r3 func(DriverConnNewStreamDoneInfo)
- if r != nil {
- r2 = r(d)
- }
- if r1 != nil {
- r3 = r1(d)
- }
- return func(d DriverConnNewStreamDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r2 != nil {
- r2(d)
- }
- if r3 != nil {
- r3(d)
- }
- }
- }
- }
- }
- {
- h1 := t.OnConnTake
- h2 := x.OnConnTake
- ret.OnConnTake = func(d DriverConnTakeStartInfo) func(DriverConnTakeDoneInfo) {
+ h1 := t.OnConnStreamCloseSend
+ h2 := x.OnConnStreamCloseSend
+ ret.OnConnStreamCloseSend = func(d DriverConnStreamCloseSendStartInfo) func(DriverConnStreamCloseSendDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -512,14 +426,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverConnTakeDoneInfo)
+ var r, r1 func(DriverConnStreamCloseSendDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverConnTakeDoneInfo) {
+ return func(d DriverConnStreamCloseSendDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -572,9 +486,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnConnPark
- h2 := x.OnConnPark
- ret.OnConnPark = func(d DriverConnParkStartInfo) func(DriverConnParkDoneInfo) {
+ h1 := t.OnConnBan
+ h2 := x.OnConnBan
+ ret.OnConnBan = func(d DriverConnBanStartInfo) func(DriverConnBanDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -582,14 +496,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverConnParkDoneInfo)
+ var r, r1 func(DriverConnBanDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverConnParkDoneInfo) {
+ return func(d DriverConnBanDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -607,9 +521,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnConnBan
- h2 := x.OnConnBan
- ret.OnConnBan = func(d DriverConnBanStartInfo) func(DriverConnBanDoneInfo) {
+ h1 := t.OnConnAllow
+ h2 := x.OnConnAllow
+ ret.OnConnAllow = func(d DriverConnAllowStartInfo) func(DriverConnAllowDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -617,14 +531,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverConnBanDoneInfo)
+ var r, r1 func(DriverConnAllowDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverConnBanDoneInfo) {
+ return func(d DriverConnAllowDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -642,9 +556,9 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
{
- h1 := t.OnConnAllow
- h2 := x.OnConnAllow
- ret.OnConnAllow = func(d DriverConnAllowStartInfo) func(DriverConnAllowDoneInfo) {
+ h1 := t.OnConnPark
+ h2 := x.OnConnPark
+ ret.OnConnPark = func(d DriverConnParkStartInfo) func(DriverConnParkDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -652,14 +566,14 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}()
}
- var r, r1 func(DriverConnAllowDoneInfo)
+ var r, r1 func(DriverConnParkDoneInfo)
if h1 != nil {
r = h1(d)
}
if h2 != nil {
r1 = h2(d)
}
- return func(d DriverConnAllowDoneInfo) {
+ return func(d DriverConnParkDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -781,41 +695,6 @@ func (t *Driver) Compose(x *Driver, opts ...DriverComposeOption) *Driver {
}
}
}
- {
- h1 := t.OnBalancerDialEntrypoint
- h2 := x.OnBalancerDialEntrypoint
- ret.OnBalancerDialEntrypoint = func(d DriverBalancerDialEntrypointStartInfo) func(DriverBalancerDialEntrypointDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- var r, r1 func(DriverBalancerDialEntrypointDoneInfo)
- if h1 != nil {
- r = h1(d)
- }
- if h2 != nil {
- r1 = h2(d)
- }
- return func(d DriverBalancerDialEntrypointDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r != nil {
- r(d)
- }
- if r1 != nil {
- r1(d)
- }
- }
- }
- }
{
h1 := t.OnBalancerClose
h2 := x.OnBalancerClose
@@ -1068,148 +947,106 @@ func (t *Driver) onPoolRelease(d DriverConnPoolReleaseStartInfo) func(DriverConn
}
return res
}
-func (t *Driver) onNetRead(d DriverNetReadStartInfo) func(DriverNetReadDoneInfo) {
- fn := t.OnNetRead
- if fn == nil {
- return func(DriverNetReadDoneInfo) {
- return
- }
- }
- res := fn(d)
- if res == nil {
- return func(DriverNetReadDoneInfo) {
- return
- }
- }
- return res
-}
-func (t *Driver) onNetWrite(d DriverNetWriteStartInfo) func(DriverNetWriteDoneInfo) {
- fn := t.OnNetWrite
+func (t *Driver) onResolve(d DriverResolveStartInfo) func(DriverResolveDoneInfo) {
+ fn := t.OnResolve
if fn == nil {
- return func(DriverNetWriteDoneInfo) {
+ return func(DriverResolveDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverNetWriteDoneInfo) {
+ return func(DriverResolveDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onNetDial(d DriverNetDialStartInfo) func(DriverNetDialDoneInfo) {
- fn := t.OnNetDial
+func (t *Driver) onConnStateChange(d DriverConnStateChangeStartInfo) func(DriverConnStateChangeDoneInfo) {
+ fn := t.OnConnStateChange
if fn == nil {
- return func(DriverNetDialDoneInfo) {
+ return func(DriverConnStateChangeDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverNetDialDoneInfo) {
+ return func(DriverConnStateChangeDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onNetClose(d DriverNetCloseStartInfo) func(DriverNetCloseDoneInfo) {
- fn := t.OnNetClose
+func (t *Driver) onConnInvoke(d DriverConnInvokeStartInfo) func(DriverConnInvokeDoneInfo) {
+ fn := t.OnConnInvoke
if fn == nil {
- return func(DriverNetCloseDoneInfo) {
+ return func(DriverConnInvokeDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverNetCloseDoneInfo) {
+ return func(DriverConnInvokeDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onResolve(d DriverResolveStartInfo) func(DriverResolveDoneInfo) {
- fn := t.OnResolve
+func (t *Driver) onConnNewStream(d DriverConnNewStreamStartInfo) func(DriverConnNewStreamDoneInfo) {
+ fn := t.OnConnNewStream
if fn == nil {
- return func(DriverResolveDoneInfo) {
+ return func(DriverConnNewStreamDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverResolveDoneInfo) {
+ return func(DriverConnNewStreamDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onConnStateChange(d DriverConnStateChangeStartInfo) func(DriverConnStateChangeDoneInfo) {
- fn := t.OnConnStateChange
+func (t *Driver) onConnStreamRecvMsg(d DriverConnStreamRecvMsgStartInfo) func(DriverConnStreamRecvMsgDoneInfo) {
+ fn := t.OnConnStreamRecvMsg
if fn == nil {
- return func(DriverConnStateChangeDoneInfo) {
+ return func(DriverConnStreamRecvMsgDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverConnStateChangeDoneInfo) {
+ return func(DriverConnStreamRecvMsgDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onConnInvoke(d DriverConnInvokeStartInfo) func(DriverConnInvokeDoneInfo) {
- fn := t.OnConnInvoke
+func (t *Driver) onConnStreamSendMsg(d DriverConnStreamSendMsgStartInfo) func(DriverConnStreamSendMsgDoneInfo) {
+ fn := t.OnConnStreamSendMsg
if fn == nil {
- return func(DriverConnInvokeDoneInfo) {
+ return func(DriverConnStreamSendMsgDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverConnInvokeDoneInfo) {
+ return func(DriverConnStreamSendMsgDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onConnNewStream(d DriverConnNewStreamStartInfo) func(DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo) {
- fn := t.OnConnNewStream
- if fn == nil {
- return func(DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo) {
- return func(DriverConnNewStreamDoneInfo) {
- return
- }
- }
- }
- res := fn(d)
- if res == nil {
- return func(DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo) {
- return func(DriverConnNewStreamDoneInfo) {
- return
- }
- }
- }
- return func(d DriverConnNewStreamRecvInfo) func(DriverConnNewStreamDoneInfo) {
- res := res(d)
- if res == nil {
- return func(DriverConnNewStreamDoneInfo) {
- return
- }
- }
- return res
- }
-}
-func (t *Driver) onConnTake(d DriverConnTakeStartInfo) func(DriverConnTakeDoneInfo) {
- fn := t.OnConnTake
+func (t *Driver) onConnStreamCloseSend(d DriverConnStreamCloseSendStartInfo) func(DriverConnStreamCloseSendDoneInfo) {
+ fn := t.OnConnStreamCloseSend
if fn == nil {
- return func(DriverConnTakeDoneInfo) {
+ return func(DriverConnStreamCloseSendDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverConnTakeDoneInfo) {
+ return func(DriverConnStreamCloseSendDoneInfo) {
return
}
}
@@ -1230,46 +1067,46 @@ func (t *Driver) onConnDial(d DriverConnDialStartInfo) func(DriverConnDialDoneIn
}
return res
}
-func (t *Driver) onConnPark(d DriverConnParkStartInfo) func(DriverConnParkDoneInfo) {
- fn := t.OnConnPark
+func (t *Driver) onConnBan(d DriverConnBanStartInfo) func(DriverConnBanDoneInfo) {
+ fn := t.OnConnBan
if fn == nil {
- return func(DriverConnParkDoneInfo) {
+ return func(DriverConnBanDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverConnParkDoneInfo) {
+ return func(DriverConnBanDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onConnBan(d DriverConnBanStartInfo) func(DriverConnBanDoneInfo) {
- fn := t.OnConnBan
+func (t *Driver) onConnAllow(d DriverConnAllowStartInfo) func(DriverConnAllowDoneInfo) {
+ fn := t.OnConnAllow
if fn == nil {
- return func(DriverConnBanDoneInfo) {
+ return func(DriverConnAllowDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverConnBanDoneInfo) {
+ return func(DriverConnAllowDoneInfo) {
return
}
}
return res
}
-func (t *Driver) onConnAllow(d DriverConnAllowStartInfo) func(DriverConnAllowDoneInfo) {
- fn := t.OnConnAllow
+func (t *Driver) onConnPark(d DriverConnParkStartInfo) func(DriverConnParkDoneInfo) {
+ fn := t.OnConnPark
if fn == nil {
- return func(DriverConnAllowDoneInfo) {
+ return func(DriverConnParkDoneInfo) {
return
}
}
res := fn(d)
if res == nil {
- return func(DriverConnAllowDoneInfo) {
+ return func(DriverConnParkDoneInfo) {
return
}
}
@@ -1320,21 +1157,6 @@ func (t *Driver) onBalancerInit(d DriverBalancerInitStartInfo) func(DriverBalanc
}
return res
}
-func (t *Driver) onBalancerDialEntrypoint(d DriverBalancerDialEntrypointStartInfo) func(DriverBalancerDialEntrypointDoneInfo) {
- fn := t.OnBalancerDialEntrypoint
- if fn == nil {
- return func(DriverBalancerDialEntrypointDoneInfo) {
- return
- }
- }
- res := fn(d)
- if res == nil {
- return func(DriverBalancerDialEntrypointDoneInfo) {
- return
- }
- }
- return res
-}
func (t *Driver) onBalancerClose(d DriverBalancerCloseStartInfo) func(DriverBalancerCloseDoneInfo) {
fn := t.OnBalancerClose
if fn == nil {
@@ -1470,55 +1292,6 @@ func DriverOnPoolRelease(t *Driver, c *context.Context, call call) func(error) {
res(p)
}
}
-func DriverOnNetRead(t *Driver, call call, address string, buffer int) func(received int, _ error) {
- var p DriverNetReadStartInfo
- p.Call = call
- p.Address = address
- p.Buffer = buffer
- res := t.onNetRead(p)
- return func(received int, e error) {
- var p DriverNetReadDoneInfo
- p.Received = received
- p.Error = e
- res(p)
- }
-}
-func DriverOnNetWrite(t *Driver, call call, address string, bytes int) func(sent int, _ error) {
- var p DriverNetWriteStartInfo
- p.Call = call
- p.Address = address
- p.Bytes = bytes
- res := t.onNetWrite(p)
- return func(sent int, e error) {
- var p DriverNetWriteDoneInfo
- p.Sent = sent
- p.Error = e
- res(p)
- }
-}
-func DriverOnNetDial(t *Driver, c *context.Context, call call, address string) func(error) {
- var p DriverNetDialStartInfo
- p.Context = c
- p.Call = call
- p.Address = address
- res := t.onNetDial(p)
- return func(e error) {
- var p DriverNetDialDoneInfo
- p.Error = e
- res(p)
- }
-}
-func DriverOnNetClose(t *Driver, call call, address string) func(error) {
- var p DriverNetCloseStartInfo
- p.Call = call
- p.Address = address
- res := t.onNetClose(p)
- return func(e error) {
- var p DriverNetCloseDoneInfo
- p.Error = e
- res(p)
- }
-}
func DriverOnResolve(t *Driver, call call, target string, resolved []string) func(error) {
var p DriverResolveStartInfo
p.Call = call
@@ -1561,58 +1334,61 @@ func DriverOnConnInvoke(t *Driver, c *context.Context, call call, endpoint Endpo
res(p)
}
}
-func DriverOnConnNewStream(t *Driver, c *context.Context, call call, endpoint EndpointInfo, m Method) func(error) func(_ error, state ConnState, metadata map[string][]string) {
+func DriverOnConnNewStream(t *Driver, c *context.Context, call call, endpoint EndpointInfo, m Method) func(_ error, state ConnState) {
var p DriverConnNewStreamStartInfo
p.Context = c
p.Call = call
p.Endpoint = endpoint
p.Method = m
res := t.onConnNewStream(p)
- return func(e error) func(error, ConnState, map[string][]string) {
- var p DriverConnNewStreamRecvInfo
+ return func(e error, state ConnState) {
+ var p DriverConnNewStreamDoneInfo
p.Error = e
- res := res(p)
- return func(e error, state ConnState, metadata map[string][]string) {
- var p DriverConnNewStreamDoneInfo
- p.Error = e
- p.State = state
- p.Metadata = metadata
- res(p)
- }
+ p.State = state
+ res(p)
}
}
-func DriverOnConnTake(t *Driver, c *context.Context, call call, endpoint EndpointInfo) func(error) {
- var p DriverConnTakeStartInfo
+func DriverOnConnStreamRecvMsg(t *Driver, c *context.Context, call call) func(error) {
+ var p DriverConnStreamRecvMsgStartInfo
p.Context = c
p.Call = call
- p.Endpoint = endpoint
- res := t.onConnTake(p)
+ res := t.onConnStreamRecvMsg(p)
return func(e error) {
- var p DriverConnTakeDoneInfo
+ var p DriverConnStreamRecvMsgDoneInfo
p.Error = e
res(p)
}
}
-func DriverOnConnDial(t *Driver, c *context.Context, call call, endpoint EndpointInfo) func(error) {
- var p DriverConnDialStartInfo
+func DriverOnConnStreamSendMsg(t *Driver, c *context.Context, call call) func(error) {
+ var p DriverConnStreamSendMsgStartInfo
p.Context = c
p.Call = call
- p.Endpoint = endpoint
- res := t.onConnDial(p)
+ res := t.onConnStreamSendMsg(p)
return func(e error) {
- var p DriverConnDialDoneInfo
+ var p DriverConnStreamSendMsgDoneInfo
p.Error = e
res(p)
}
}
-func DriverOnConnPark(t *Driver, c *context.Context, call call, endpoint EndpointInfo) func(error) {
- var p DriverConnParkStartInfo
+func DriverOnConnStreamCloseSend(t *Driver, c *context.Context, call call) func(error) {
+ var p DriverConnStreamCloseSendStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onConnStreamCloseSend(p)
+ return func(e error) {
+ var p DriverConnStreamCloseSendDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func DriverOnConnDial(t *Driver, c *context.Context, call call, endpoint EndpointInfo) func(error) {
+ var p DriverConnDialStartInfo
p.Context = c
p.Call = call
p.Endpoint = endpoint
- res := t.onConnPark(p)
+ res := t.onConnDial(p)
return func(e error) {
- var p DriverConnParkDoneInfo
+ var p DriverConnDialDoneInfo
p.Error = e
res(p)
}
@@ -1644,6 +1420,18 @@ func DriverOnConnAllow(t *Driver, c *context.Context, call call, endpoint Endpoi
res(p)
}
}
+func DriverOnConnPark(t *Driver, c *context.Context, call call, endpoint EndpointInfo) func(error) {
+ var p DriverConnParkStartInfo
+ p.Context = c
+ p.Call = call
+ p.Endpoint = endpoint
+ res := t.onConnPark(p)
+ return func(e error) {
+ var p DriverConnParkDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
func DriverOnConnClose(t *Driver, c *context.Context, call call, endpoint EndpointInfo) func(error) {
var p DriverConnCloseStartInfo
p.Context = c
@@ -1681,18 +1469,6 @@ func DriverOnBalancerInit(t *Driver, c *context.Context, call call, name string)
res(p)
}
}
-func DriverOnBalancerDialEntrypoint(t *Driver, c *context.Context, call call, address string) func(error) {
- var p DriverBalancerDialEntrypointStartInfo
- p.Context = c
- p.Call = call
- p.Address = address
- res := t.onBalancerDialEntrypoint(p)
- return func(e error) {
- var p DriverBalancerDialEntrypointDoneInfo
- p.Error = e
- res(p)
- }
-}
func DriverOnBalancerClose(t *Driver, c *context.Context, call call) func(error) {
var p DriverBalancerCloseStartInfo
p.Context = c
@@ -1728,19 +1504,18 @@ func DriverOnBalancerClusterDiscoveryAttempt(t *Driver, c *context.Context, call
res(p)
}
}
-func DriverOnBalancerUpdate(t *Driver, c *context.Context, call call, needLocalDC bool) func(endpoints []EndpointInfo, added []EndpointInfo, dropped []EndpointInfo, localDC string, _ error) {
+func DriverOnBalancerUpdate(t *Driver, c *context.Context, call call, needLocalDC bool) func(endpoints []EndpointInfo, added []EndpointInfo, dropped []EndpointInfo, localDC string) {
var p DriverBalancerUpdateStartInfo
p.Context = c
p.Call = call
p.NeedLocalDC = needLocalDC
res := t.onBalancerUpdate(p)
- return func(endpoints []EndpointInfo, added []EndpointInfo, dropped []EndpointInfo, localDC string, e error) {
+ return func(endpoints []EndpointInfo, added []EndpointInfo, dropped []EndpointInfo, localDC string) {
var p DriverBalancerUpdateDoneInfo
p.Endpoints = endpoints
p.Added = added
p.Dropped = dropped
p.LocalDC = localDC
- p.Error = e
res(p)
}
}
diff --git a/trace/query.go b/trace/query.go
new file mode 100644
index 000000000..aefb54ddd
--- /dev/null
+++ b/trace/query.go
@@ -0,0 +1,338 @@
+package trace
+
+import (
+ "context"
+)
+
+// tool gtrace used from ./internal/cmd/gtrace
+
+//go:generate gtrace
+
+type (
+ querySessionInfo interface {
+ ID() string
+ NodeID() int64
+ Status() string
+ }
+ queryTransactionInfo interface {
+ ID() string
+ }
+
+ // Query specified trace of retry call activity.
+ // gtrace:gen
+ Query struct {
+ OnNew func(QueryNewStartInfo) func(info QueryNewDoneInfo)
+ OnClose func(QueryCloseStartInfo) func(info QueryCloseDoneInfo)
+
+ OnPoolNew func(QueryPoolNewStartInfo) func(QueryPoolNewDoneInfo)
+ OnPoolClose func(QueryPoolCloseStartInfo) func(QueryPoolCloseDoneInfo)
+ OnPoolTry func(QueryPoolTryStartInfo) func(QueryPoolTryDoneInfo)
+ OnPoolWith func(QueryPoolWithStartInfo) func(QueryPoolWithDoneInfo)
+ OnPoolPut func(QueryPoolPutStartInfo) func(QueryPoolPutDoneInfo)
+ OnPoolGet func(QueryPoolGetStartInfo) func(QueryPoolGetDoneInfo)
+ OnPoolChange func(QueryPoolChange)
+
+ OnDo func(QueryDoStartInfo) func(QueryDoDoneInfo)
+ OnDoTx func(QueryDoTxStartInfo) func(QueryDoTxDoneInfo)
+
+ OnSessionCreate func(QuerySessionCreateStartInfo) func(info QuerySessionCreateDoneInfo)
+ OnSessionAttach func(QuerySessionAttachStartInfo) func(info QuerySessionAttachDoneInfo)
+ OnSessionDelete func(QuerySessionDeleteStartInfo) func(info QuerySessionDeleteDoneInfo)
+ OnSessionExecute func(QuerySessionExecuteStartInfo) func(info QuerySessionExecuteDoneInfo)
+ OnSessionBegin func(QuerySessionBeginStartInfo) func(info QuerySessionBeginDoneInfo)
+ OnTxExecute func(QueryTxExecuteStartInfo) func(info QueryTxExecuteDoneInfo)
+ OnResultNew func(QueryResultNewStartInfo) func(info QueryResultNewDoneInfo)
+ OnResultNextPart func(QueryResultNextPartStartInfo) func(info QueryResultNextPartDoneInfo)
+ OnResultNextResultSet func(QueryResultNextResultSetStartInfo) func(info QueryResultNextResultSetDoneInfo)
+ OnResultClose func(QueryResultCloseStartInfo) func(info QueryResultCloseDoneInfo)
+ OnResultSetNextRow func(QueryResultSetNextRowStartInfo) func(info QueryResultSetNextRowDoneInfo)
+ OnRowScan func(QueryRowScanStartInfo) func(info QueryRowScanDoneInfo)
+ OnRowScanNamed func(QueryRowScanNamedStartInfo) func(info QueryRowScanNamedDoneInfo)
+ OnRowScanStruct func(QueryRowScanStructStartInfo) func(info QueryRowScanStructDoneInfo)
+ }
+
+ QueryDoStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryDoDoneInfo struct {
+ Attempts int
+ Error error
+ }
+ QueryDoTxStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryDoTxDoneInfo struct {
+ Attempts int
+ Error error
+ }
+ QuerySessionCreateStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QuerySessionCreateDoneInfo struct {
+ Session querySessionInfo
+ Error error
+ }
+ QuerySessionExecuteStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Session querySessionInfo
+ Query string
+ }
+ QuerySessionExecuteDoneInfo struct {
+ Error error
+ }
+ QueryTxExecuteStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+
+ Session querySessionInfo
+ Tx queryTransactionInfo
+ Query string
+ }
+ QueryTxExecuteDoneInfo struct {
+ Error error
+ }
+ QuerySessionAttachStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ Session querySessionInfo
+ }
+ QuerySessionAttachDoneInfo struct {
+ Error error
+ }
+ QuerySessionBeginStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ Session querySessionInfo
+ }
+ QuerySessionBeginDoneInfo struct {
+ Error error
+ Tx queryTransactionInfo
+ }
+ QueryResultNewStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryResultNewDoneInfo struct {
+ Error error
+ }
+ QueryResultCloseStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryResultCloseDoneInfo struct {
+ Error error
+ }
+ QueryResultNextPartStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryResultNextPartDoneInfo struct {
+ Error error
+ }
+ QueryResultNextResultSetStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryResultNextResultSetDoneInfo struct {
+ Error error
+ }
+ QueryResultSetNextRowStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryResultSetNextRowDoneInfo struct {
+ Error error
+ }
+ QueryRowScanStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryRowScanDoneInfo struct {
+ Error error
+ }
+ QueryRowScanNamedStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryRowScanNamedDoneInfo struct {
+ Error error
+ }
+ QueryRowScanStructStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryRowScanStructDoneInfo struct {
+ Error error
+ }
+ QuerySessionDeleteStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ Session querySessionInfo
+ }
+ QuerySessionDeleteDoneInfo struct {
+ Error error
+ }
+ QueryNewStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryNewDoneInfo struct{}
+ QueryCloseStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryCloseDoneInfo struct {
+ Error error
+ }
+ QueryPoolNewStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryPoolNewDoneInfo struct {
+ Limit int
+ }
+ QueryPoolCloseStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryPoolCloseDoneInfo struct {
+ Error error
+ }
+ QueryPoolTryStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryPoolTryDoneInfo struct {
+ Error error
+ }
+ QueryPoolWithStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryPoolWithDoneInfo struct {
+ Error error
+
+ Attempts int
+ }
+ QueryPoolPutStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryPoolPutDoneInfo struct {
+ Error error
+ }
+ QueryPoolGetStartInfo struct {
+ // Context make available context in trace callback function.
+ // Pointer to context provide replacement of context in trace callback function.
+ // Warning: concurrent access to pointer on client side must be excluded.
+ // Safe replacement of context are provided only inside callback function
+ Context *context.Context
+ Call call
+ }
+ QueryPoolGetDoneInfo struct {
+ Error error
+ }
+ QueryPoolChange struct {
+ Limit int
+ Index int
+ Idle int
+ InUse int
+ }
+)
diff --git a/trace/query_gtrace.go b/trace/query_gtrace.go
new file mode 100644
index 000000000..2353b77b4
--- /dev/null
+++ b/trace/query_gtrace.go
@@ -0,0 +1,1544 @@
+// Code generated by gtrace. DO NOT EDIT.
+
+package trace
+
+import (
+ "context"
+)
+
+// queryComposeOptions is a holder of options
+type queryComposeOptions struct {
+ panicCallback func(e interface{})
+}
+
+// QueryOption specified Query compose option
+type QueryComposeOption func(o *queryComposeOptions)
+
+// WithQueryPanicCallback specified behavior on panic
+func WithQueryPanicCallback(cb func(e interface{})) QueryComposeOption {
+ return func(o *queryComposeOptions) {
+ o.panicCallback = cb
+ }
+}
+
+// Compose returns a new Query which has functional fields composed both from t and x.
+func (t *Query) Compose(x *Query, opts ...QueryComposeOption) *Query {
+ var ret Query
+ options := queryComposeOptions{}
+ for _, opt := range opts {
+ if opt != nil {
+ opt(&options)
+ }
+ }
+ {
+ h1 := t.OnNew
+ h2 := x.OnNew
+ ret.OnNew = func(q QueryNewStartInfo) func(QueryNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryNewDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnClose
+ h2 := x.OnClose
+ ret.OnClose = func(q QueryCloseStartInfo) func(QueryCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryCloseDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolNew
+ h2 := x.OnPoolNew
+ ret.OnPoolNew = func(q QueryPoolNewStartInfo) func(QueryPoolNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryPoolNewDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryPoolNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolClose
+ h2 := x.OnPoolClose
+ ret.OnPoolClose = func(q QueryPoolCloseStartInfo) func(QueryPoolCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryPoolCloseDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryPoolCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolTry
+ h2 := x.OnPoolTry
+ ret.OnPoolTry = func(q QueryPoolTryStartInfo) func(QueryPoolTryDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryPoolTryDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryPoolTryDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolWith
+ h2 := x.OnPoolWith
+ ret.OnPoolWith = func(q QueryPoolWithStartInfo) func(QueryPoolWithDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryPoolWithDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryPoolWithDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolPut
+ h2 := x.OnPoolPut
+ ret.OnPoolPut = func(q QueryPoolPutStartInfo) func(QueryPoolPutDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryPoolPutDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryPoolPutDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolGet
+ h2 := x.OnPoolGet
+ ret.OnPoolGet = func(q QueryPoolGetStartInfo) func(QueryPoolGetDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryPoolGetDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryPoolGetDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnPoolChange
+ h2 := x.OnPoolChange
+ ret.OnPoolChange = func(q QueryPoolChange) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if h1 != nil {
+ h1(q)
+ }
+ if h2 != nil {
+ h2(q)
+ }
+ }
+ }
+ {
+ h1 := t.OnDo
+ h2 := x.OnDo
+ ret.OnDo = func(q QueryDoStartInfo) func(QueryDoDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryDoDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryDoDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnDoTx
+ h2 := x.OnDoTx
+ ret.OnDoTx = func(q QueryDoTxStartInfo) func(QueryDoTxDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryDoTxDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(q QueryDoTxDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(q)
+ }
+ if r1 != nil {
+ r1(q)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionCreate
+ h2 := x.OnSessionCreate
+ ret.OnSessionCreate = func(q QuerySessionCreateStartInfo) func(QuerySessionCreateDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QuerySessionCreateDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QuerySessionCreateDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionAttach
+ h2 := x.OnSessionAttach
+ ret.OnSessionAttach = func(q QuerySessionAttachStartInfo) func(QuerySessionAttachDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QuerySessionAttachDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QuerySessionAttachDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionDelete
+ h2 := x.OnSessionDelete
+ ret.OnSessionDelete = func(q QuerySessionDeleteStartInfo) func(QuerySessionDeleteDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QuerySessionDeleteDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QuerySessionDeleteDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionExecute
+ h2 := x.OnSessionExecute
+ ret.OnSessionExecute = func(q QuerySessionExecuteStartInfo) func(QuerySessionExecuteDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QuerySessionExecuteDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QuerySessionExecuteDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnSessionBegin
+ h2 := x.OnSessionBegin
+ ret.OnSessionBegin = func(q QuerySessionBeginStartInfo) func(QuerySessionBeginDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QuerySessionBeginDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QuerySessionBeginDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnTxExecute
+ h2 := x.OnTxExecute
+ ret.OnTxExecute = func(q QueryTxExecuteStartInfo) func(QueryTxExecuteDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryTxExecuteDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryTxExecuteDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnResultNew
+ h2 := x.OnResultNew
+ ret.OnResultNew = func(q QueryResultNewStartInfo) func(QueryResultNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryResultNewDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryResultNewDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnResultNextPart
+ h2 := x.OnResultNextPart
+ ret.OnResultNextPart = func(q QueryResultNextPartStartInfo) func(QueryResultNextPartDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryResultNextPartDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryResultNextPartDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnResultNextResultSet
+ h2 := x.OnResultNextResultSet
+ ret.OnResultNextResultSet = func(q QueryResultNextResultSetStartInfo) func(QueryResultNextResultSetDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryResultNextResultSetDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryResultNextResultSetDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnResultClose
+ h2 := x.OnResultClose
+ ret.OnResultClose = func(q QueryResultCloseStartInfo) func(QueryResultCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryResultCloseDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryResultCloseDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnResultSetNextRow
+ h2 := x.OnResultSetNextRow
+ ret.OnResultSetNextRow = func(q QueryResultSetNextRowStartInfo) func(QueryResultSetNextRowDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryResultSetNextRowDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryResultSetNextRowDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnRowScan
+ h2 := x.OnRowScan
+ ret.OnRowScan = func(q QueryRowScanStartInfo) func(QueryRowScanDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryRowScanDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryRowScanDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnRowScanNamed
+ h2 := x.OnRowScanNamed
+ ret.OnRowScanNamed = func(q QueryRowScanNamedStartInfo) func(QueryRowScanNamedDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryRowScanNamedDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryRowScanNamedDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ {
+ h1 := t.OnRowScanStruct
+ h2 := x.OnRowScanStruct
+ ret.OnRowScanStruct = func(q QueryRowScanStructStartInfo) func(QueryRowScanStructDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ var r, r1 func(QueryRowScanStructDoneInfo)
+ if h1 != nil {
+ r = h1(q)
+ }
+ if h2 != nil {
+ r1 = h2(q)
+ }
+ return func(info QueryRowScanStructDoneInfo) {
+ if options.panicCallback != nil {
+ defer func() {
+ if e := recover(); e != nil {
+ options.panicCallback(e)
+ }
+ }()
+ }
+ if r != nil {
+ r(info)
+ }
+ if r1 != nil {
+ r1(info)
+ }
+ }
+ }
+ }
+ return &ret
+}
+func (t *Query) onNew(q QueryNewStartInfo) func(info QueryNewDoneInfo) {
+ fn := t.OnNew
+ if fn == nil {
+ return func(QueryNewDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryNewDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onClose(q QueryCloseStartInfo) func(info QueryCloseDoneInfo) {
+ fn := t.OnClose
+ if fn == nil {
+ return func(QueryCloseDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryCloseDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolNew(q QueryPoolNewStartInfo) func(QueryPoolNewDoneInfo) {
+ fn := t.OnPoolNew
+ if fn == nil {
+ return func(QueryPoolNewDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryPoolNewDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolClose(q QueryPoolCloseStartInfo) func(QueryPoolCloseDoneInfo) {
+ fn := t.OnPoolClose
+ if fn == nil {
+ return func(QueryPoolCloseDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryPoolCloseDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolTry(q QueryPoolTryStartInfo) func(QueryPoolTryDoneInfo) {
+ fn := t.OnPoolTry
+ if fn == nil {
+ return func(QueryPoolTryDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryPoolTryDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolWith(q QueryPoolWithStartInfo) func(QueryPoolWithDoneInfo) {
+ fn := t.OnPoolWith
+ if fn == nil {
+ return func(QueryPoolWithDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryPoolWithDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolPut(q QueryPoolPutStartInfo) func(QueryPoolPutDoneInfo) {
+ fn := t.OnPoolPut
+ if fn == nil {
+ return func(QueryPoolPutDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryPoolPutDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolGet(q QueryPoolGetStartInfo) func(QueryPoolGetDoneInfo) {
+ fn := t.OnPoolGet
+ if fn == nil {
+ return func(QueryPoolGetDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryPoolGetDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onPoolChange(q QueryPoolChange) {
+ fn := t.OnPoolChange
+ if fn == nil {
+ return
+ }
+ fn(q)
+}
+func (t *Query) onDo(q QueryDoStartInfo) func(QueryDoDoneInfo) {
+ fn := t.OnDo
+ if fn == nil {
+ return func(QueryDoDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryDoDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onDoTx(q QueryDoTxStartInfo) func(QueryDoTxDoneInfo) {
+ fn := t.OnDoTx
+ if fn == nil {
+ return func(QueryDoTxDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryDoTxDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onSessionCreate(q QuerySessionCreateStartInfo) func(info QuerySessionCreateDoneInfo) {
+ fn := t.OnSessionCreate
+ if fn == nil {
+ return func(QuerySessionCreateDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QuerySessionCreateDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onSessionAttach(q QuerySessionAttachStartInfo) func(info QuerySessionAttachDoneInfo) {
+ fn := t.OnSessionAttach
+ if fn == nil {
+ return func(QuerySessionAttachDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QuerySessionAttachDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onSessionDelete(q QuerySessionDeleteStartInfo) func(info QuerySessionDeleteDoneInfo) {
+ fn := t.OnSessionDelete
+ if fn == nil {
+ return func(QuerySessionDeleteDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QuerySessionDeleteDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onSessionExecute(q QuerySessionExecuteStartInfo) func(info QuerySessionExecuteDoneInfo) {
+ fn := t.OnSessionExecute
+ if fn == nil {
+ return func(QuerySessionExecuteDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QuerySessionExecuteDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onSessionBegin(q QuerySessionBeginStartInfo) func(info QuerySessionBeginDoneInfo) {
+ fn := t.OnSessionBegin
+ if fn == nil {
+ return func(QuerySessionBeginDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QuerySessionBeginDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onTxExecute(q QueryTxExecuteStartInfo) func(info QueryTxExecuteDoneInfo) {
+ fn := t.OnTxExecute
+ if fn == nil {
+ return func(QueryTxExecuteDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryTxExecuteDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onResultNew(q QueryResultNewStartInfo) func(info QueryResultNewDoneInfo) {
+ fn := t.OnResultNew
+ if fn == nil {
+ return func(QueryResultNewDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryResultNewDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onResultNextPart(q QueryResultNextPartStartInfo) func(info QueryResultNextPartDoneInfo) {
+ fn := t.OnResultNextPart
+ if fn == nil {
+ return func(QueryResultNextPartDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryResultNextPartDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onResultNextResultSet(q QueryResultNextResultSetStartInfo) func(info QueryResultNextResultSetDoneInfo) {
+ fn := t.OnResultNextResultSet
+ if fn == nil {
+ return func(QueryResultNextResultSetDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryResultNextResultSetDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onResultClose(q QueryResultCloseStartInfo) func(info QueryResultCloseDoneInfo) {
+ fn := t.OnResultClose
+ if fn == nil {
+ return func(QueryResultCloseDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryResultCloseDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onResultSetNextRow(q QueryResultSetNextRowStartInfo) func(info QueryResultSetNextRowDoneInfo) {
+ fn := t.OnResultSetNextRow
+ if fn == nil {
+ return func(QueryResultSetNextRowDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryResultSetNextRowDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onRowScan(q QueryRowScanStartInfo) func(info QueryRowScanDoneInfo) {
+ fn := t.OnRowScan
+ if fn == nil {
+ return func(QueryRowScanDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryRowScanDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onRowScanNamed(q QueryRowScanNamedStartInfo) func(info QueryRowScanNamedDoneInfo) {
+ fn := t.OnRowScanNamed
+ if fn == nil {
+ return func(QueryRowScanNamedDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryRowScanNamedDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func (t *Query) onRowScanStruct(q QueryRowScanStructStartInfo) func(info QueryRowScanStructDoneInfo) {
+ fn := t.OnRowScanStruct
+ if fn == nil {
+ return func(QueryRowScanStructDoneInfo) {
+ return
+ }
+ }
+ res := fn(q)
+ if res == nil {
+ return func(QueryRowScanStructDoneInfo) {
+ return
+ }
+ }
+ return res
+}
+func QueryOnNew(t *Query, c *context.Context, call call) func() {
+ var p QueryNewStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onNew(p)
+ return func() {
+ var p QueryNewDoneInfo
+ res(p)
+ }
+}
+func QueryOnClose(t *Query, c *context.Context, call call) func(error) {
+ var p QueryCloseStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onClose(p)
+ return func(e error) {
+ var p QueryCloseDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnPoolNew(t *Query, c *context.Context, call call) func(limit int) {
+ var p QueryPoolNewStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onPoolNew(p)
+ return func(limit int) {
+ var p QueryPoolNewDoneInfo
+ p.Limit = limit
+ res(p)
+ }
+}
+func QueryOnPoolClose(t *Query, c *context.Context, call call) func(error) {
+ var p QueryPoolCloseStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onPoolClose(p)
+ return func(e error) {
+ var p QueryPoolCloseDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnPoolTry(t *Query, c *context.Context, call call) func(error) {
+ var p QueryPoolTryStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onPoolTry(p)
+ return func(e error) {
+ var p QueryPoolTryDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnPoolWith(t *Query, c *context.Context, call call) func(_ error, attempts int) {
+ var p QueryPoolWithStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onPoolWith(p)
+ return func(e error, attempts int) {
+ var p QueryPoolWithDoneInfo
+ p.Error = e
+ p.Attempts = attempts
+ res(p)
+ }
+}
+func QueryOnPoolPut(t *Query, c *context.Context, call call) func(error) {
+ var p QueryPoolPutStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onPoolPut(p)
+ return func(e error) {
+ var p QueryPoolPutDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnPoolGet(t *Query, c *context.Context, call call) func(error) {
+ var p QueryPoolGetStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onPoolGet(p)
+ return func(e error) {
+ var p QueryPoolGetDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnPoolChange(t *Query, limit int, index int, idle int, inUse int) {
+ var p QueryPoolChange
+ p.Limit = limit
+ p.Index = index
+ p.Idle = idle
+ p.InUse = inUse
+ t.onPoolChange(p)
+}
+func QueryOnDo(t *Query, c *context.Context, call call) func(attempts int, _ error) {
+ var p QueryDoStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onDo(p)
+ return func(attempts int, e error) {
+ var p QueryDoDoneInfo
+ p.Attempts = attempts
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnDoTx(t *Query, c *context.Context, call call) func(attempts int, _ error) {
+ var p QueryDoTxStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onDoTx(p)
+ return func(attempts int, e error) {
+ var p QueryDoTxDoneInfo
+ p.Attempts = attempts
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnSessionCreate(t *Query, c *context.Context, call call) func(session querySessionInfo, _ error) {
+ var p QuerySessionCreateStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onSessionCreate(p)
+ return func(session querySessionInfo, e error) {
+ var p QuerySessionCreateDoneInfo
+ p.Session = session
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnSessionAttach(t *Query, c *context.Context, call call, session querySessionInfo) func(error) {
+ var p QuerySessionAttachStartInfo
+ p.Context = c
+ p.Call = call
+ p.Session = session
+ res := t.onSessionAttach(p)
+ return func(e error) {
+ var p QuerySessionAttachDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnSessionDelete(t *Query, c *context.Context, call call, session querySessionInfo) func(error) {
+ var p QuerySessionDeleteStartInfo
+ p.Context = c
+ p.Call = call
+ p.Session = session
+ res := t.onSessionDelete(p)
+ return func(e error) {
+ var p QuerySessionDeleteDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnSessionExecute(t *Query, c *context.Context, call call, session querySessionInfo, query string) func(error) {
+ var p QuerySessionExecuteStartInfo
+ p.Context = c
+ p.Call = call
+ p.Session = session
+ p.Query = query
+ res := t.onSessionExecute(p)
+ return func(e error) {
+ var p QuerySessionExecuteDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnSessionBegin(t *Query, c *context.Context, call call, session querySessionInfo) func(_ error, tx queryTransactionInfo) {
+ var p QuerySessionBeginStartInfo
+ p.Context = c
+ p.Call = call
+ p.Session = session
+ res := t.onSessionBegin(p)
+ return func(e error, tx queryTransactionInfo) {
+ var p QuerySessionBeginDoneInfo
+ p.Error = e
+ p.Tx = tx
+ res(p)
+ }
+}
+func QueryOnTxExecute(t *Query, c *context.Context, call call, session querySessionInfo, tx queryTransactionInfo, query string) func(error) {
+ var p QueryTxExecuteStartInfo
+ p.Context = c
+ p.Call = call
+ p.Session = session
+ p.Tx = tx
+ p.Query = query
+ res := t.onTxExecute(p)
+ return func(e error) {
+ var p QueryTxExecuteDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnResultNew(t *Query, c *context.Context, call call) func(error) {
+ var p QueryResultNewStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onResultNew(p)
+ return func(e error) {
+ var p QueryResultNewDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnResultNextPart(t *Query, c *context.Context, call call) func(error) {
+ var p QueryResultNextPartStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onResultNextPart(p)
+ return func(e error) {
+ var p QueryResultNextPartDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnResultNextResultSet(t *Query, c *context.Context, call call) func(error) {
+ var p QueryResultNextResultSetStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onResultNextResultSet(p)
+ return func(e error) {
+ var p QueryResultNextResultSetDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnResultClose(t *Query, c *context.Context, call call) func(error) {
+ var p QueryResultCloseStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onResultClose(p)
+ return func(e error) {
+ var p QueryResultCloseDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnResultSetNextRow(t *Query, c *context.Context, call call) func(error) {
+ var p QueryResultSetNextRowStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onResultSetNextRow(p)
+ return func(e error) {
+ var p QueryResultSetNextRowDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnRowScan(t *Query, c *context.Context, call call) func(error) {
+ var p QueryRowScanStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onRowScan(p)
+ return func(e error) {
+ var p QueryRowScanDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnRowScanNamed(t *Query, c *context.Context, call call) func(error) {
+ var p QueryRowScanNamedStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onRowScanNamed(p)
+ return func(e error) {
+ var p QueryRowScanNamedDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
+func QueryOnRowScanStruct(t *Query, c *context.Context, call call) func(error) {
+ var p QueryRowScanStructStartInfo
+ p.Context = c
+ p.Call = call
+ res := t.onRowScanStruct(p)
+ return func(e error) {
+ var p QueryRowScanStructDoneInfo
+ p.Error = e
+ res(p)
+ }
+}
diff --git a/trace/retry.go b/trace/retry.go
index 0ac2c483b..c87a149d2 100644
--- a/trace/retry.go
+++ b/trace/retry.go
@@ -12,7 +12,7 @@ type (
// Retry specified trace of retry call activity.
// gtrace:gen
Retry struct {
- OnRetry func(RetryLoopStartInfo) func(RetryLoopIntermediateInfo) func(RetryLoopDoneInfo)
+ OnRetry func(RetryLoopStartInfo) func(RetryLoopDoneInfo)
}
RetryLoopStartInfo struct {
// Context make available context in trace callback function.
@@ -21,18 +21,12 @@ type (
// Safe replacement of context are provided only inside callback function
Context *context.Context
- // Deprecated: use Label field instead
- ID string
-
Call call
Label string
Idempotent bool
NestedCall bool // a sign for detect Retry calls inside head Retry
}
- RetryLoopIntermediateInfo struct {
- Error error
- }
RetryLoopDoneInfo struct {
Attempts int
Error error
diff --git a/trace/retry_gtrace.go b/trace/retry_gtrace.go
index ea80a385f..14a35e413 100644
--- a/trace/retry_gtrace.go
+++ b/trace/retry_gtrace.go
@@ -33,7 +33,7 @@ func (t *Retry) Compose(x *Retry, opts ...RetryComposeOption) *Retry {
{
h1 := t.OnRetry
h2 := x.OnRetry
- ret.OnRetry = func(r RetryLoopStartInfo) func(RetryLoopIntermediateInfo) func(RetryLoopDoneInfo) {
+ ret.OnRetry = func(r RetryLoopStartInfo) func(RetryLoopDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -41,14 +41,14 @@ func (t *Retry) Compose(x *Retry, opts ...RetryComposeOption) *Retry {
}
}()
}
- var r1, r2 func(RetryLoopIntermediateInfo) func(RetryLoopDoneInfo)
+ var r1, r2 func(RetryLoopDoneInfo)
if h1 != nil {
r1 = h1(r)
}
if h2 != nil {
r2 = h2(r)
}
- return func(r RetryLoopIntermediateInfo) func(RetryLoopDoneInfo) {
+ return func(r RetryLoopDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -56,78 +56,44 @@ func (t *Retry) Compose(x *Retry, opts ...RetryComposeOption) *Retry {
}
}()
}
- var r3, r4 func(RetryLoopDoneInfo)
if r1 != nil {
- r3 = r1(r)
+ r1(r)
}
if r2 != nil {
- r4 = r2(r)
- }
- return func(r RetryLoopDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r3 != nil {
- r3(r)
- }
- if r4 != nil {
- r4(r)
- }
+ r2(r)
}
}
}
}
return &ret
}
-func (t *Retry) onRetry(r RetryLoopStartInfo) func(RetryLoopIntermediateInfo) func(RetryLoopDoneInfo) {
+func (t *Retry) onRetry(r RetryLoopStartInfo) func(RetryLoopDoneInfo) {
fn := t.OnRetry
if fn == nil {
- return func(RetryLoopIntermediateInfo) func(RetryLoopDoneInfo) {
- return func(RetryLoopDoneInfo) {
- return
- }
+ return func(RetryLoopDoneInfo) {
+ return
}
}
res := fn(r)
if res == nil {
- return func(RetryLoopIntermediateInfo) func(RetryLoopDoneInfo) {
- return func(RetryLoopDoneInfo) {
- return
- }
+ return func(RetryLoopDoneInfo) {
+ return
}
}
- return func(r RetryLoopIntermediateInfo) func(RetryLoopDoneInfo) {
- res := res(r)
- if res == nil {
- return func(RetryLoopDoneInfo) {
- return
- }
- }
- return res
- }
+ return res
}
-func RetryOnRetry(t *Retry, c *context.Context, iD string, call call, label string, idempotent bool, nestedCall bool) func(error) func(attempts int, _ error) {
+func RetryOnRetry(t *Retry, c *context.Context, call call, label string, idempotent bool, nestedCall bool) func(attempts int, _ error) {
var p RetryLoopStartInfo
p.Context = c
- p.ID = iD
p.Call = call
p.Label = label
p.Idempotent = idempotent
p.NestedCall = nestedCall
res := t.onRetry(p)
- return func(e error) func(int, error) {
- var p RetryLoopIntermediateInfo
+ return func(attempts int, e error) {
+ var p RetryLoopDoneInfo
+ p.Attempts = attempts
p.Error = e
- res := res(p)
- return func(attempts int, e error) {
- var p RetryLoopDoneInfo
- p.Attempts = attempts
- p.Error = e
- res(p)
- }
+ res(p)
}
}
diff --git a/trace/sql.go b/trace/sql.go
index d98c905b5..e6d5de6ec 100644
--- a/trace/sql.go
+++ b/trace/sql.go
@@ -159,9 +159,6 @@ type (
TxContext context.Context
Tx tableTransactionInfo
Query string
-
- // Deprecated: all transactions are idempotent
- Idempotent bool
}
DatabaseSQLTxQueryDoneInfo struct {
Error error
@@ -176,9 +173,6 @@ type (
TxContext context.Context
Tx tableTransactionInfo
Query string
-
- // Deprecated: all transactions are idempotent
- Idempotent bool
}
DatabaseSQLTxExecDoneInfo struct {
Error error
diff --git a/trace/sql_gtrace.go b/trace/sql_gtrace.go
index a9e59a2ef..e5d2a484f 100644
--- a/trace/sql_gtrace.go
+++ b/trace/sql_gtrace.go
@@ -1012,14 +1012,13 @@ func DatabaseSQLOnConnIsTableExists(t *DatabaseSQL, c *context.Context, call cal
res(p)
}
}
-func DatabaseSQLOnTxQuery(t *DatabaseSQL, c *context.Context, call call, txContext context.Context, tx tableTransactionInfo, query string, idempotent bool) func(error) {
+func DatabaseSQLOnTxQuery(t *DatabaseSQL, c *context.Context, call call, txContext context.Context, tx tableTransactionInfo, query string) func(error) {
var p DatabaseSQLTxQueryStartInfo
p.Context = c
p.Call = call
p.TxContext = txContext
p.Tx = tx
p.Query = query
- p.Idempotent = idempotent
res := t.onTxQuery(p)
return func(e error) {
var p DatabaseSQLTxQueryDoneInfo
@@ -1027,14 +1026,13 @@ func DatabaseSQLOnTxQuery(t *DatabaseSQL, c *context.Context, call call, txConte
res(p)
}
}
-func DatabaseSQLOnTxExec(t *DatabaseSQL, c *context.Context, call call, txContext context.Context, tx tableTransactionInfo, query string, idempotent bool) func(error) {
+func DatabaseSQLOnTxExec(t *DatabaseSQL, c *context.Context, call call, txContext context.Context, tx tableTransactionInfo, query string) func(error) {
var p DatabaseSQLTxExecStartInfo
p.Context = c
p.Call = call
p.TxContext = txContext
p.Tx = tx
p.Query = query
- p.Idempotent = idempotent
res := t.onTxExec(p)
return func(e error) {
var p DatabaseSQLTxExecDoneInfo
diff --git a/trace/table.go b/trace/table.go
index 81e298324..4e944435a 100644
--- a/trace/table.go
+++ b/trace/table.go
@@ -16,15 +16,9 @@ type (
// Client events
OnInit func(TableInitStartInfo) func(TableInitDoneInfo)
OnClose func(TableCloseStartInfo) func(TableCloseDoneInfo)
- OnDo func(TableDoStartInfo) func(info TableDoIntermediateInfo) func(TableDoDoneInfo)
- OnDoTx func(TableDoTxStartInfo) func(info TableDoTxIntermediateInfo) func(TableDoTxDoneInfo)
- OnCreateSession func(
- TableCreateSessionStartInfo,
- ) func(
- info TableCreateSessionIntermediateInfo,
- ) func(
- TableCreateSessionDoneInfo,
- )
+ OnDo func(TableDoStartInfo) func(TableDoDoneInfo)
+ OnDoTx func(TableDoTxStartInfo) func(TableDoTxDoneInfo)
+ OnCreateSession func(TableCreateSessionStartInfo) func(TableCreateSessionDoneInfo)
// Session events
OnSessionNew func(TableSessionNewStartInfo) func(TableSessionNewDoneInfo)
OnSessionDelete func(TableSessionDeleteStartInfo) func(TableSessionDeleteDoneInfo)
@@ -35,36 +29,22 @@ type (
OnSessionQueryExecute func(TableExecuteDataQueryStartInfo) func(TableExecuteDataQueryDoneInfo)
OnSessionQueryExplain func(TableExplainQueryStartInfo) func(TableExplainQueryDoneInfo)
// Stream events
- OnSessionQueryStreamExecute func(
- TableSessionQueryStreamExecuteStartInfo,
- ) func(
- TableSessionQueryStreamExecuteIntermediateInfo,
- ) func(
- TableSessionQueryStreamExecuteDoneInfo,
- )
- OnSessionQueryStreamRead func(
- TableSessionQueryStreamReadStartInfo,
- ) func(
- TableSessionQueryStreamReadIntermediateInfo,
- ) func(
- TableSessionQueryStreamReadDoneInfo,
- )
+ OnSessionQueryStreamExecute func(TableSessionQueryStreamExecuteStartInfo) func(TableSessionQueryStreamExecuteDoneInfo)
+ OnSessionQueryStreamRead func(TableSessionQueryStreamReadStartInfo) func(TableSessionQueryStreamReadDoneInfo)
// Transaction events
- OnSessionTransactionBegin func(TableSessionTransactionBeginStartInfo) func(
- TableSessionTransactionBeginDoneInfo,
+ OnTxBegin func(TableTxBeginStartInfo) func(
+ TableTxBeginDoneInfo,
)
- OnSessionTransactionExecute func(TableTransactionExecuteStartInfo) func(
+ OnTxExecute func(TableTransactionExecuteStartInfo) func(
TableTransactionExecuteDoneInfo,
)
- OnSessionTransactionExecuteStatement func(TableTransactionExecuteStatementStartInfo) func(
+ OnTxExecuteStatement func(TableTransactionExecuteStatementStartInfo) func(
TableTransactionExecuteStatementDoneInfo,
)
- OnSessionTransactionCommit func(TableSessionTransactionCommitStartInfo) func(
- TableSessionTransactionCommitDoneInfo,
- )
- OnSessionTransactionRollback func(TableSessionTransactionRollbackStartInfo) func(
- TableSessionTransactionRollbackDoneInfo,
+ OnTxCommit func(TableTxCommitStartInfo) func(
+ TableTxCommitDoneInfo,
)
+ OnTxRollback func(TableTxRollbackStartInfo) func(TableTxRollbackDoneInfo)
// Pool state event
OnPoolStateChange func(TablePoolStateChangeInfo)
@@ -72,16 +52,6 @@ type (
OnPoolSessionAdd func(info TablePoolSessionAddInfo)
OnPoolSessionRemove func(info TablePoolSessionRemoveInfo)
- // OnPoolSessionNew is user-defined callback for listening events about creating sessions with
- // internal session pool calls
- //
- // Deprecated: use OnPoolSessionAdd callback
- OnPoolSessionNew func(TablePoolSessionNewStartInfo) func(TablePoolSessionNewDoneInfo)
-
- // OnPoolSessionClose is user-defined callback for listening sessionClose calls
- //
- // Deprecated: use OnPoolSessionRemove callback
- OnPoolSessionClose func(TablePoolSessionCloseStartInfo) func(TablePoolSessionCloseDoneInfo)
// Pool common API events
OnPoolPut func(TablePoolPutStartInfo) func(TablePoolPutDoneInfo)
OnPoolGet func(TablePoolGetStartInfo) func(TablePoolGetDoneInfo)
@@ -250,9 +220,6 @@ type (
Call call
Session tableSessionInfo
}
- TableSessionQueryStreamReadIntermediateInfo struct {
- Error error
- }
TableSessionQueryStreamReadDoneInfo struct {
Error error
}
@@ -267,13 +234,10 @@ type (
Query tableDataQuery
Parameters tableQueryParameters
}
- TableSessionQueryStreamExecuteIntermediateInfo struct {
- Error error
- }
TableSessionQueryStreamExecuteDoneInfo struct {
Error error
}
- TableSessionTransactionBeginStartInfo struct {
+ TableTxBeginStartInfo struct {
// Context make available context in trace callback function.
// Pointer to context provide replacement of context in trace callback function.
// Warning: concurrent access to pointer on client side must be excluded.
@@ -282,11 +246,11 @@ type (
Call call
Session tableSessionInfo
}
- TableSessionTransactionBeginDoneInfo struct {
+ TableTxBeginDoneInfo struct {
Tx tableTransactionInfo
Error error
}
- TableSessionTransactionCommitStartInfo struct {
+ TableTxCommitStartInfo struct {
// Context make available context in trace callback function.
// Pointer to context provide replacement of context in trace callback function.
// Warning: concurrent access to pointer on client side must be excluded.
@@ -296,10 +260,10 @@ type (
Session tableSessionInfo
Tx tableTransactionInfo
}
- TableSessionTransactionCommitDoneInfo struct {
+ TableTxCommitDoneInfo struct {
Error error
}
- TableSessionTransactionRollbackStartInfo struct {
+ TableTxRollbackStartInfo struct {
// Context make available context in trace callback function.
// Pointer to context provide replacement of context in trace callback function.
// Warning: concurrent access to pointer on client side must be excluded.
@@ -309,7 +273,7 @@ type (
Session tableSessionInfo
Tx tableTransactionInfo
}
- TableSessionTransactionRollbackDoneInfo struct {
+ TableTxRollbackDoneInfo struct {
Error error
}
TableInitStartInfo struct {
@@ -322,7 +286,6 @@ type (
}
TableInitDoneInfo struct {
Limit int
- Error error
}
TablePoolStateChangeInfo struct {
Size int
@@ -415,16 +378,10 @@ type (
Context *context.Context
Call call
- // Deprecated: use Label field instead
- ID string
-
Label string
Idempotent bool
NestedCall bool // flag when Retry called inside head Retry
}
- TableDoIntermediateInfo struct {
- Error error
- }
TableDoDoneInfo struct {
Attempts int
Error error
@@ -437,16 +394,10 @@ type (
Context *context.Context
Call call
- // Deprecated: use Label field instead
- ID string
-
Label string
Idempotent bool
NestedCall bool // flag when Retry called inside head Retry
}
- TableDoTxIntermediateInfo struct {
- Error error
- }
TableDoTxDoneInfo struct {
Attempts int
Error error
@@ -459,9 +410,6 @@ type (
Context *context.Context
Call call
}
- TableCreateSessionIntermediateInfo struct {
- Error error
- }
TableCreateSessionDoneInfo struct {
Session tableSessionInfo
Attempts int
diff --git a/trace/table_gtrace.go b/trace/table_gtrace.go
index 95630c8f8..6834d38b9 100644
--- a/trace/table_gtrace.go
+++ b/trace/table_gtrace.go
@@ -103,7 +103,7 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
{
h1 := t.OnDo
h2 := x.OnDo
- ret.OnDo = func(t TableDoStartInfo) func(TableDoIntermediateInfo) func(TableDoDoneInfo) {
+ ret.OnDo = func(t TableDoStartInfo) func(TableDoDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -111,14 +111,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableDoIntermediateInfo) func(TableDoDoneInfo)
+ var r, r1 func(TableDoDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(info TableDoIntermediateInfo) func(TableDoDoneInfo) {
+ return func(t TableDoDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -126,27 +126,11 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r2, r3 func(TableDoDoneInfo)
if r != nil {
- r2 = r(info)
+ r(t)
}
if r1 != nil {
- r3 = r1(info)
- }
- return func(t TableDoDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r2 != nil {
- r2(t)
- }
- if r3 != nil {
- r3(t)
- }
+ r1(t)
}
}
}
@@ -154,7 +138,7 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
{
h1 := t.OnDoTx
h2 := x.OnDoTx
- ret.OnDoTx = func(t TableDoTxStartInfo) func(TableDoTxIntermediateInfo) func(TableDoTxDoneInfo) {
+ ret.OnDoTx = func(t TableDoTxStartInfo) func(TableDoTxDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -162,14 +146,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableDoTxIntermediateInfo) func(TableDoTxDoneInfo)
+ var r, r1 func(TableDoTxDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(info TableDoTxIntermediateInfo) func(TableDoTxDoneInfo) {
+ return func(t TableDoTxDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -177,27 +161,11 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r2, r3 func(TableDoTxDoneInfo)
if r != nil {
- r2 = r(info)
+ r(t)
}
if r1 != nil {
- r3 = r1(info)
- }
- return func(t TableDoTxDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r2 != nil {
- r2(t)
- }
- if r3 != nil {
- r3(t)
- }
+ r1(t)
}
}
}
@@ -205,7 +173,7 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
{
h1 := t.OnCreateSession
h2 := x.OnCreateSession
- ret.OnCreateSession = func(t TableCreateSessionStartInfo) func(TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo) {
+ ret.OnCreateSession = func(t TableCreateSessionStartInfo) func(TableCreateSessionDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -213,14 +181,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo)
+ var r, r1 func(TableCreateSessionDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(info TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo) {
+ return func(t TableCreateSessionDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -228,27 +196,11 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r2, r3 func(TableCreateSessionDoneInfo)
if r != nil {
- r2 = r(info)
+ r(t)
}
if r1 != nil {
- r3 = r1(info)
- }
- return func(t TableCreateSessionDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r2 != nil {
- r2(t)
- }
- if r3 != nil {
- r3(t)
- }
+ r1(t)
}
}
}
@@ -501,7 +453,7 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
{
h1 := t.OnSessionQueryStreamExecute
h2 := x.OnSessionQueryStreamExecute
- ret.OnSessionQueryStreamExecute = func(t TableSessionQueryStreamExecuteStartInfo) func(TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
+ ret.OnSessionQueryStreamExecute = func(t TableSessionQueryStreamExecuteStartInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -509,14 +461,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo)
+ var r, r1 func(TableSessionQueryStreamExecuteDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(t TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
+ return func(t TableSessionQueryStreamExecuteDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -524,27 +476,11 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r2, r3 func(TableSessionQueryStreamExecuteDoneInfo)
if r != nil {
- r2 = r(t)
+ r(t)
}
if r1 != nil {
- r3 = r1(t)
- }
- return func(t TableSessionQueryStreamExecuteDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r2 != nil {
- r2(t)
- }
- if r3 != nil {
- r3(t)
- }
+ r1(t)
}
}
}
@@ -552,7 +488,7 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
{
h1 := t.OnSessionQueryStreamRead
h2 := x.OnSessionQueryStreamRead
- ret.OnSessionQueryStreamRead = func(t TableSessionQueryStreamReadStartInfo) func(TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo) {
+ ret.OnSessionQueryStreamRead = func(t TableSessionQueryStreamReadStartInfo) func(TableSessionQueryStreamReadDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -560,14 +496,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo)
+ var r, r1 func(TableSessionQueryStreamReadDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(t TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo) {
+ return func(t TableSessionQueryStreamReadDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -575,35 +511,19 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r2, r3 func(TableSessionQueryStreamReadDoneInfo)
if r != nil {
- r2 = r(t)
+ r(t)
}
if r1 != nil {
- r3 = r1(t)
- }
- return func(t TableSessionQueryStreamReadDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r2 != nil {
- r2(t)
- }
- if r3 != nil {
- r3(t)
- }
+ r1(t)
}
}
}
}
{
- h1 := t.OnSessionTransactionBegin
- h2 := x.OnSessionTransactionBegin
- ret.OnSessionTransactionBegin = func(t TableSessionTransactionBeginStartInfo) func(TableSessionTransactionBeginDoneInfo) {
+ h1 := t.OnTxBegin
+ h2 := x.OnTxBegin
+ ret.OnTxBegin = func(t TableTxBeginStartInfo) func(TableTxBeginDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -611,14 +531,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableSessionTransactionBeginDoneInfo)
+ var r, r1 func(TableTxBeginDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(t TableSessionTransactionBeginDoneInfo) {
+ return func(t TableTxBeginDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -636,9 +556,9 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}
{
- h1 := t.OnSessionTransactionExecute
- h2 := x.OnSessionTransactionExecute
- ret.OnSessionTransactionExecute = func(t TableTransactionExecuteStartInfo) func(TableTransactionExecuteDoneInfo) {
+ h1 := t.OnTxExecute
+ h2 := x.OnTxExecute
+ ret.OnTxExecute = func(t TableTransactionExecuteStartInfo) func(TableTransactionExecuteDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -671,9 +591,9 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}
{
- h1 := t.OnSessionTransactionExecuteStatement
- h2 := x.OnSessionTransactionExecuteStatement
- ret.OnSessionTransactionExecuteStatement = func(t TableTransactionExecuteStatementStartInfo) func(TableTransactionExecuteStatementDoneInfo) {
+ h1 := t.OnTxExecuteStatement
+ h2 := x.OnTxExecuteStatement
+ ret.OnTxExecuteStatement = func(t TableTransactionExecuteStatementStartInfo) func(TableTransactionExecuteStatementDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -706,9 +626,9 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}
{
- h1 := t.OnSessionTransactionCommit
- h2 := x.OnSessionTransactionCommit
- ret.OnSessionTransactionCommit = func(t TableSessionTransactionCommitStartInfo) func(TableSessionTransactionCommitDoneInfo) {
+ h1 := t.OnTxCommit
+ h2 := x.OnTxCommit
+ ret.OnTxCommit = func(t TableTxCommitStartInfo) func(TableTxCommitDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -716,14 +636,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableSessionTransactionCommitDoneInfo)
+ var r, r1 func(TableTxCommitDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(t TableSessionTransactionCommitDoneInfo) {
+ return func(t TableTxCommitDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -741,9 +661,9 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}
{
- h1 := t.OnSessionTransactionRollback
- h2 := x.OnSessionTransactionRollback
- ret.OnSessionTransactionRollback = func(t TableSessionTransactionRollbackStartInfo) func(TableSessionTransactionRollbackDoneInfo) {
+ h1 := t.OnTxRollback
+ h2 := x.OnTxRollback
+ ret.OnTxRollback = func(t TableTxRollbackStartInfo) func(TableTxRollbackDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -751,14 +671,14 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}()
}
- var r, r1 func(TableSessionTransactionRollbackDoneInfo)
+ var r, r1 func(TableTxRollbackDoneInfo)
if h1 != nil {
r = h1(t)
}
if h2 != nil {
r1 = h2(t)
}
- return func(t TableSessionTransactionRollbackDoneInfo) {
+ return func(t TableTxRollbackDoneInfo) {
if options.panicCallback != nil {
defer func() {
if e := recover(); e != nil {
@@ -832,76 +752,6 @@ func (t *Table) Compose(x *Table, opts ...TableComposeOption) *Table {
}
}
}
- {
- h1 := t.OnPoolSessionNew
- h2 := x.OnPoolSessionNew
- ret.OnPoolSessionNew = func(t TablePoolSessionNewStartInfo) func(TablePoolSessionNewDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- var r, r1 func(TablePoolSessionNewDoneInfo)
- if h1 != nil {
- r = h1(t)
- }
- if h2 != nil {
- r1 = h2(t)
- }
- return func(t TablePoolSessionNewDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r != nil {
- r(t)
- }
- if r1 != nil {
- r1(t)
- }
- }
- }
- }
- {
- h1 := t.OnPoolSessionClose
- h2 := x.OnPoolSessionClose
- ret.OnPoolSessionClose = func(t TablePoolSessionCloseStartInfo) func(TablePoolSessionCloseDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- var r, r1 func(TablePoolSessionCloseDoneInfo)
- if h1 != nil {
- r = h1(t)
- }
- if h2 != nil {
- r1 = h2(t)
- }
- return func(t TablePoolSessionCloseDoneInfo) {
- if options.panicCallback != nil {
- defer func() {
- if e := recover(); e != nil {
- options.panicCallback(e)
- }
- }()
- }
- if r != nil {
- r(t)
- }
- if r1 != nil {
- r1(t)
- }
- }
- }
- }
{
h1 := t.OnPoolPut
h2 := x.OnPoolPut
@@ -1039,86 +889,50 @@ func (t *Table) onClose(t1 TableCloseStartInfo) func(TableCloseDoneInfo) {
}
return res
}
-func (t *Table) onDo(t1 TableDoStartInfo) func(info TableDoIntermediateInfo) func(TableDoDoneInfo) {
+func (t *Table) onDo(t1 TableDoStartInfo) func(TableDoDoneInfo) {
fn := t.OnDo
if fn == nil {
- return func(TableDoIntermediateInfo) func(TableDoDoneInfo) {
- return func(TableDoDoneInfo) {
- return
- }
+ return func(TableDoDoneInfo) {
+ return
}
}
res := fn(t1)
if res == nil {
- return func(TableDoIntermediateInfo) func(TableDoDoneInfo) {
- return func(TableDoDoneInfo) {
- return
- }
- }
- }
- return func(info TableDoIntermediateInfo) func(TableDoDoneInfo) {
- res := res(info)
- if res == nil {
- return func(TableDoDoneInfo) {
- return
- }
+ return func(TableDoDoneInfo) {
+ return
}
- return res
}
+ return res
}
-func (t *Table) onDoTx(t1 TableDoTxStartInfo) func(info TableDoTxIntermediateInfo) func(TableDoTxDoneInfo) {
+func (t *Table) onDoTx(t1 TableDoTxStartInfo) func(TableDoTxDoneInfo) {
fn := t.OnDoTx
if fn == nil {
- return func(TableDoTxIntermediateInfo) func(TableDoTxDoneInfo) {
- return func(TableDoTxDoneInfo) {
- return
- }
+ return func(TableDoTxDoneInfo) {
+ return
}
}
res := fn(t1)
if res == nil {
- return func(TableDoTxIntermediateInfo) func(TableDoTxDoneInfo) {
- return func(TableDoTxDoneInfo) {
- return
- }
- }
- }
- return func(info TableDoTxIntermediateInfo) func(TableDoTxDoneInfo) {
- res := res(info)
- if res == nil {
- return func(TableDoTxDoneInfo) {
- return
- }
+ return func(TableDoTxDoneInfo) {
+ return
}
- return res
}
+ return res
}
-func (t *Table) onCreateSession(t1 TableCreateSessionStartInfo) func(info TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo) {
+func (t *Table) onCreateSession(t1 TableCreateSessionStartInfo) func(TableCreateSessionDoneInfo) {
fn := t.OnCreateSession
if fn == nil {
- return func(TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo) {
- return func(TableCreateSessionDoneInfo) {
- return
- }
+ return func(TableCreateSessionDoneInfo) {
+ return
}
}
res := fn(t1)
if res == nil {
- return func(TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo) {
- return func(TableCreateSessionDoneInfo) {
- return
- }
- }
- }
- return func(info TableCreateSessionIntermediateInfo) func(TableCreateSessionDoneInfo) {
- res := res(info)
- if res == nil {
- return func(TableCreateSessionDoneInfo) {
- return
- }
+ return func(TableCreateSessionDoneInfo) {
+ return
}
- return res
}
+ return res
}
func (t *Table) onSessionNew(t1 TableSessionNewStartInfo) func(TableSessionNewDoneInfo) {
fn := t.OnSessionNew
@@ -1225,77 +1039,53 @@ func (t *Table) onSessionQueryExplain(t1 TableExplainQueryStartInfo) func(TableE
}
return res
}
-func (t *Table) onSessionQueryStreamExecute(t1 TableSessionQueryStreamExecuteStartInfo) func(TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
+func (t *Table) onSessionQueryStreamExecute(t1 TableSessionQueryStreamExecuteStartInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
fn := t.OnSessionQueryStreamExecute
if fn == nil {
- return func(TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
- return func(TableSessionQueryStreamExecuteDoneInfo) {
- return
- }
+ return func(TableSessionQueryStreamExecuteDoneInfo) {
+ return
}
}
res := fn(t1)
if res == nil {
- return func(TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
- return func(TableSessionQueryStreamExecuteDoneInfo) {
- return
- }
- }
- }
- return func(t TableSessionQueryStreamExecuteIntermediateInfo) func(TableSessionQueryStreamExecuteDoneInfo) {
- res := res(t)
- if res == nil {
- return func(TableSessionQueryStreamExecuteDoneInfo) {
- return
- }
+ return func(TableSessionQueryStreamExecuteDoneInfo) {
+ return
}
- return res
}
+ return res
}
-func (t *Table) onSessionQueryStreamRead(t1 TableSessionQueryStreamReadStartInfo) func(TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo) {
+func (t *Table) onSessionQueryStreamRead(t1 TableSessionQueryStreamReadStartInfo) func(TableSessionQueryStreamReadDoneInfo) {
fn := t.OnSessionQueryStreamRead
if fn == nil {
- return func(TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo) {
- return func(TableSessionQueryStreamReadDoneInfo) {
- return
- }
+ return func(TableSessionQueryStreamReadDoneInfo) {
+ return
}
}
res := fn(t1)
if res == nil {
- return func(TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo) {
- return func(TableSessionQueryStreamReadDoneInfo) {
- return
- }
- }
- }
- return func(t TableSessionQueryStreamReadIntermediateInfo) func(TableSessionQueryStreamReadDoneInfo) {
- res := res(t)
- if res == nil {
- return func(TableSessionQueryStreamReadDoneInfo) {
- return
- }
+ return func(TableSessionQueryStreamReadDoneInfo) {
+ return
}
- return res
}
+ return res
}
-func (t *Table) onSessionTransactionBegin(t1 TableSessionTransactionBeginStartInfo) func(TableSessionTransactionBeginDoneInfo) {
- fn := t.OnSessionTransactionBegin
+func (t *Table) onTxBegin(t1 TableTxBeginStartInfo) func(TableTxBeginDoneInfo) {
+ fn := t.OnTxBegin
if fn == nil {
- return func(TableSessionTransactionBeginDoneInfo) {
+ return func(TableTxBeginDoneInfo) {
return
}
}
res := fn(t1)
if res == nil {
- return func(TableSessionTransactionBeginDoneInfo) {
+ return func(TableTxBeginDoneInfo) {
return
}
}
return res
}
-func (t *Table) onSessionTransactionExecute(t1 TableTransactionExecuteStartInfo) func(TableTransactionExecuteDoneInfo) {
- fn := t.OnSessionTransactionExecute
+func (t *Table) onTxExecute(t1 TableTransactionExecuteStartInfo) func(TableTransactionExecuteDoneInfo) {
+ fn := t.OnTxExecute
if fn == nil {
return func(TableTransactionExecuteDoneInfo) {
return
@@ -1309,8 +1099,8 @@ func (t *Table) onSessionTransactionExecute(t1 TableTransactionExecuteStartInfo)
}
return res
}
-func (t *Table) onSessionTransactionExecuteStatement(t1 TableTransactionExecuteStatementStartInfo) func(TableTransactionExecuteStatementDoneInfo) {
- fn := t.OnSessionTransactionExecuteStatement
+func (t *Table) onTxExecuteStatement(t1 TableTransactionExecuteStatementStartInfo) func(TableTransactionExecuteStatementDoneInfo) {
+ fn := t.OnTxExecuteStatement
if fn == nil {
return func(TableTransactionExecuteStatementDoneInfo) {
return
@@ -1324,31 +1114,31 @@ func (t *Table) onSessionTransactionExecuteStatement(t1 TableTransactionExecuteS
}
return res
}
-func (t *Table) onSessionTransactionCommit(t1 TableSessionTransactionCommitStartInfo) func(TableSessionTransactionCommitDoneInfo) {
- fn := t.OnSessionTransactionCommit
+func (t *Table) onTxCommit(t1 TableTxCommitStartInfo) func(TableTxCommitDoneInfo) {
+ fn := t.OnTxCommit
if fn == nil {
- return func(TableSessionTransactionCommitDoneInfo) {
+ return func(TableTxCommitDoneInfo) {
return
}
}
res := fn(t1)
if res == nil {
- return func(TableSessionTransactionCommitDoneInfo) {
+ return func(TableTxCommitDoneInfo) {
return
}
}
return res
}
-func (t *Table) onSessionTransactionRollback(t1 TableSessionTransactionRollbackStartInfo) func(TableSessionTransactionRollbackDoneInfo) {
- fn := t.OnSessionTransactionRollback
+func (t *Table) onTxRollback(t1 TableTxRollbackStartInfo) func(TableTxRollbackDoneInfo) {
+ fn := t.OnTxRollback
if fn == nil {
- return func(TableSessionTransactionRollbackDoneInfo) {
+ return func(TableTxRollbackDoneInfo) {
return
}
}
res := fn(t1)
if res == nil {
- return func(TableSessionTransactionRollbackDoneInfo) {
+ return func(TableTxRollbackDoneInfo) {
return
}
}
@@ -1375,36 +1165,6 @@ func (t *Table) onPoolSessionRemove(info TablePoolSessionRemoveInfo) {
}
fn(info)
}
-func (t *Table) onPoolSessionNew(t1 TablePoolSessionNewStartInfo) func(TablePoolSessionNewDoneInfo) {
- fn := t.OnPoolSessionNew
- if fn == nil {
- return func(TablePoolSessionNewDoneInfo) {
- return
- }
- }
- res := fn(t1)
- if res == nil {
- return func(TablePoolSessionNewDoneInfo) {
- return
- }
- }
- return res
-}
-func (t *Table) onPoolSessionClose(t1 TablePoolSessionCloseStartInfo) func(TablePoolSessionCloseDoneInfo) {
- fn := t.OnPoolSessionClose
- if fn == nil {
- return func(TablePoolSessionCloseDoneInfo) {
- return
- }
- }
- res := fn(t1)
- if res == nil {
- return func(TablePoolSessionCloseDoneInfo) {
- return
- }
- }
- return res
-}
func (t *Table) onPoolPut(t1 TablePoolPutStartInfo) func(TablePoolPutDoneInfo) {
fn := t.OnPoolPut
if fn == nil {
@@ -1450,15 +1210,14 @@ func (t *Table) onPoolWait(t1 TablePoolWaitStartInfo) func(TablePoolWaitDoneInfo
}
return res
}
-func TableOnInit(t *Table, c *context.Context, call call) func(limit int, _ error) {
+func TableOnInit(t *Table, c *context.Context, call call) func(limit int) {
var p TableInitStartInfo
p.Context = c
p.Call = call
res := t.onInit(p)
- return func(limit int, e error) {
+ return func(limit int) {
var p TableInitDoneInfo
p.Limit = limit
- p.Error = e
res(p)
}
}
@@ -1473,64 +1232,47 @@ func TableOnClose(t *Table, c *context.Context, call call) func(error) {
res(p)
}
}
-func TableOnDo(t *Table, c *context.Context, call call, iD string, label string, idempotent bool, nestedCall bool) func(error) func(attempts int, _ error) {
+func TableOnDo(t *Table, c *context.Context, call call, label string, idempotent bool, nestedCall bool) func(attempts int, _ error) {
var p TableDoStartInfo
p.Context = c
p.Call = call
- p.ID = iD
p.Label = label
p.Idempotent = idempotent
p.NestedCall = nestedCall
res := t.onDo(p)
- return func(e error) func(int, error) {
- var p TableDoIntermediateInfo
+ return func(attempts int, e error) {
+ var p TableDoDoneInfo
+ p.Attempts = attempts
p.Error = e
- res := res(p)
- return func(attempts int, e error) {
- var p TableDoDoneInfo
- p.Attempts = attempts
- p.Error = e
- res(p)
- }
+ res(p)
}
}
-func TableOnDoTx(t *Table, c *context.Context, call call, iD string, label string, idempotent bool, nestedCall bool) func(error) func(attempts int, _ error) {
+func TableOnDoTx(t *Table, c *context.Context, call call, label string, idempotent bool, nestedCall bool) func(attempts int, _ error) {
var p TableDoTxStartInfo
p.Context = c
p.Call = call
- p.ID = iD
p.Label = label
p.Idempotent = idempotent
p.NestedCall = nestedCall
res := t.onDoTx(p)
- return func(e error) func(int, error) {
- var p TableDoTxIntermediateInfo
+ return func(attempts int, e error) {
+ var p TableDoTxDoneInfo
+ p.Attempts = attempts
p.Error = e
- res := res(p)
- return func(attempts int, e error) {
- var p TableDoTxDoneInfo
- p.Attempts = attempts
- p.Error = e
- res(p)
- }
+ res(p)
}
}
-func TableOnCreateSession(t *Table, c *context.Context, call call) func(error) func(session tableSessionInfo, attempts int, _ error) {
+func TableOnCreateSession(t *Table, c *context.Context, call call) func(session tableSessionInfo, attempts int, _ error) {
var p TableCreateSessionStartInfo
p.Context = c
p.Call = call
res := t.onCreateSession(p)
- return func(e error) func(tableSessionInfo, int, error) {
- var p TableCreateSessionIntermediateInfo
+ return func(session tableSessionInfo, attempts int, e error) {
+ var p TableCreateSessionDoneInfo
+ p.Session = session
+ p.Attempts = attempts
p.Error = e
- res := res(p)
- return func(session tableSessionInfo, attempts int, e error) {
- var p TableCreateSessionDoneInfo
- p.Session = session
- p.Attempts = attempts
- p.Error = e
- res(p)
- }
+ res(p)
}
}
func TableOnSessionNew(t *Table, c *context.Context, call call) func(session tableSessionInfo, _ error) {
@@ -1628,7 +1370,7 @@ func TableOnSessionQueryExplain(t *Table, c *context.Context, call call, session
res(p)
}
}
-func TableOnSessionQueryStreamExecute(t *Table, c *context.Context, call call, session tableSessionInfo, query tableDataQuery, parameters tableQueryParameters) func(error) func(error) {
+func TableOnSessionQueryStreamExecute(t *Table, c *context.Context, call call, session tableSessionInfo, query tableDataQuery, parameters tableQueryParameters) func(error) {
var p TableSessionQueryStreamExecuteStartInfo
p.Context = c
p.Call = call
@@ -1636,48 +1378,38 @@ func TableOnSessionQueryStreamExecute(t *Table, c *context.Context, call call, s
p.Query = query
p.Parameters = parameters
res := t.onSessionQueryStreamExecute(p)
- return func(e error) func(error) {
- var p TableSessionQueryStreamExecuteIntermediateInfo
+ return func(e error) {
+ var p TableSessionQueryStreamExecuteDoneInfo
p.Error = e
- res := res(p)
- return func(e error) {
- var p TableSessionQueryStreamExecuteDoneInfo
- p.Error = e
- res(p)
- }
+ res(p)
}
}
-func TableOnSessionQueryStreamRead(t *Table, c *context.Context, call call, session tableSessionInfo) func(error) func(error) {
+func TableOnSessionQueryStreamRead(t *Table, c *context.Context, call call, session tableSessionInfo) func(error) {
var p TableSessionQueryStreamReadStartInfo
p.Context = c
p.Call = call
p.Session = session
res := t.onSessionQueryStreamRead(p)
- return func(e error) func(error) {
- var p TableSessionQueryStreamReadIntermediateInfo
+ return func(e error) {
+ var p TableSessionQueryStreamReadDoneInfo
p.Error = e
- res := res(p)
- return func(e error) {
- var p TableSessionQueryStreamReadDoneInfo
- p.Error = e
- res(p)
- }
+ res(p)
}
}
-func TableOnSessionTransactionBegin(t *Table, c *context.Context, call call, session tableSessionInfo) func(tx tableTransactionInfo, _ error) {
- var p TableSessionTransactionBeginStartInfo
+func TableOnTxBegin(t *Table, c *context.Context, call call, session tableSessionInfo) func(tx tableTransactionInfo, _ error) {
+ var p TableTxBeginStartInfo
p.Context = c
p.Call = call
p.Session = session
- res := t.onSessionTransactionBegin(p)
+ res := t.onTxBegin(p)
return func(tx tableTransactionInfo, e error) {
- var p TableSessionTransactionBeginDoneInfo
+ var p TableTxBeginDoneInfo
p.Tx = tx
p.Error = e
res(p)
}
}
-func TableOnSessionTransactionExecute(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo, query tableDataQuery, parameters tableQueryParameters) func(result tableResult, _ error) {
+func TableOnTxExecute(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo, query tableDataQuery, parameters tableQueryParameters) func(result tableResult, _ error) {
var p TableTransactionExecuteStartInfo
p.Context = c
p.Call = call
@@ -1685,7 +1417,7 @@ func TableOnSessionTransactionExecute(t *Table, c *context.Context, call call, s
p.Tx = tx
p.Query = query
p.Parameters = parameters
- res := t.onSessionTransactionExecute(p)
+ res := t.onTxExecute(p)
return func(result tableResult, e error) {
var p TableTransactionExecuteDoneInfo
p.Result = result
@@ -1693,7 +1425,7 @@ func TableOnSessionTransactionExecute(t *Table, c *context.Context, call call, s
res(p)
}
}
-func TableOnSessionTransactionExecuteStatement(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo, statementQuery tableDataQuery, parameters tableQueryParameters) func(result tableResult, _ error) {
+func TableOnTxExecuteStatement(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo, statementQuery tableDataQuery, parameters tableQueryParameters) func(result tableResult, _ error) {
var p TableTransactionExecuteStatementStartInfo
p.Context = c
p.Call = call
@@ -1701,7 +1433,7 @@ func TableOnSessionTransactionExecuteStatement(t *Table, c *context.Context, cal
p.Tx = tx
p.StatementQuery = statementQuery
p.Parameters = parameters
- res := t.onSessionTransactionExecuteStatement(p)
+ res := t.onTxExecuteStatement(p)
return func(result tableResult, e error) {
var p TableTransactionExecuteStatementDoneInfo
p.Result = result
@@ -1709,28 +1441,28 @@ func TableOnSessionTransactionExecuteStatement(t *Table, c *context.Context, cal
res(p)
}
}
-func TableOnSessionTransactionCommit(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo) func(error) {
- var p TableSessionTransactionCommitStartInfo
+func TableOnTxCommit(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo) func(error) {
+ var p TableTxCommitStartInfo
p.Context = c
p.Call = call
p.Session = session
p.Tx = tx
- res := t.onSessionTransactionCommit(p)
+ res := t.onTxCommit(p)
return func(e error) {
- var p TableSessionTransactionCommitDoneInfo
+ var p TableTxCommitDoneInfo
p.Error = e
res(p)
}
}
-func TableOnSessionTransactionRollback(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo) func(error) {
- var p TableSessionTransactionRollbackStartInfo
+func TableOnTxRollback(t *Table, c *context.Context, call call, session tableSessionInfo, tx tableTransactionInfo) func(error) {
+ var p TableTxRollbackStartInfo
p.Context = c
p.Call = call
p.Session = session
p.Tx = tx
- res := t.onSessionTransactionRollback(p)
+ res := t.onTxRollback(p)
return func(e error) {
- var p TableSessionTransactionRollbackDoneInfo
+ var p TableTxRollbackDoneInfo
p.Error = e
res(p)
}
@@ -1751,29 +1483,6 @@ func TableOnPoolSessionRemove(t *Table, session tableSessionInfo) {
p.Session = session
t.onPoolSessionRemove(p)
}
-func TableOnPoolSessionNew(t *Table, c *context.Context, call call) func(session tableSessionInfo, _ error) {
- var p TablePoolSessionNewStartInfo
- p.Context = c
- p.Call = call
- res := t.onPoolSessionNew(p)
- return func(session tableSessionInfo, e error) {
- var p TablePoolSessionNewDoneInfo
- p.Session = session
- p.Error = e
- res(p)
- }
-}
-func TableOnPoolSessionClose(t *Table, c *context.Context, call call, session tableSessionInfo) func() {
- var p TablePoolSessionCloseStartInfo
- p.Context = c
- p.Call = call
- p.Session = session
- res := t.onPoolSessionClose(p)
- return func() {
- var p TablePoolSessionCloseDoneInfo
- res(p)
- }
-}
func TableOnPoolPut(t *Table, c *context.Context, call call, session tableSessionInfo) func(error) {
var p TablePoolPutStartInfo
p.Context = c
diff --git a/with.go b/with.go
index 248ae9435..c226db991 100644
--- a/with.go
+++ b/with.go
@@ -49,7 +49,7 @@ func (d *Driver) With(ctx context.Context, opts ...Option) (*Driver, error) {
onDone := trace.DriverOnWith(
d.trace(), &ctx,
- stack.FunctionID(""),
+ stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/ydb.(*Driver).With"),
d.config.Endpoint(), d.config.Database(), d.config.Secure(),
)
defer func() {