diff --git a/.github/scripts/format-go-code.sh b/.github/scripts/format-go-code.sh index 33063ce42..03420092e 100755 --- a/.github/scripts/format-go-code.sh +++ b/.github/scripts/format-go-code.sh @@ -6,12 +6,15 @@ FILEPATH="$1" gofmt -s -w "$FILEPATH" -# https://github.com/rinchsan/gosimports +# https://github.com/daixiang0/gci if [[ "$FILEPATH" == *"tests/slo/"* ]] then - gosimports -local slo -w "$FILEPATH" + gci write --skip-generated -s standard -s default -s "prefix(slo)" "$FILEPATH" +elif [[ "$FILEPATH" == *"examples/"* ]] +then + gci write --skip-generated -s standard -s default -s "prefix(examples)" "$FILEPATH" else - gosimports -local github.com/ydb-platform/ydb-go-sdk/v3 -w "$FILEPATH" + gci write --skip-generated -s standard -s default -s "prefix(github.com/ydb-platform/ydb-go-sdk/v3)" "$FILEPATH" fi 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 a4be45c6f..0ee6bd406 100644 --- a/.github/workflows/check-codegen.yml +++ b/.github/workflows/check-codegen.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - go-version: [1.20.x, 1.21.x] + go-version: [1.21.x, 1.22.x] runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -32,7 +32,8 @@ jobs: - name: Build run: | go install ./internal/cmd/gtrace - go install go.uber.org/mock/mockgen@892b665398ecece7c7a900d2a2f2fa3dac07e4c4 + go install ./internal/cmd/gstack + go install go.uber.org/mock/mockgen@v0.4.0 - name: Clean and re-generate *_gtrace.go files run: | @@ -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/lint.yml b/.github/workflows/lint.yml index d62b7849e..c4b89aa7a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -69,7 +69,7 @@ jobs: - name: Install utilities run: | go install mvdan.cc/gofumpt@v0.3.1 - go install github.com/rinchsan/gosimports/cmd/gosimports@v0.1.5 + go install github.com/daixiang0/gci@v0.12.1 - name: format all files with auto-formatter run: bash ./.github/scripts/format-all-go-code.sh "$PWD" - name: Check repository diff diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fb3f63aa4..61361f822 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -70,7 +70,7 @@ jobs: TAG="v$MAJOR.$MINOR.$PATCH"; fi; git tag $TAG - git push --tags && git push + git push && git push --tags CHANGELOG="$CHANGELOG Full Changelog: [$LAST_TAG...$TAG](https://github.com/ydb-platform/ydb-go-sdk/compare/$LAST_TAG...$TAG)" 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 d9c87d217..756b2fd38 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.20.x, 1.21.x] + go-version: [1.21.x, 1.22.x] os: [ubuntu, windows, macOS] env: OS: ${{ matrix.os }}-latest @@ -44,11 +44,11 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.20.x, 1.21.x] - ydb-version: [22.5, 23.1, 23.2, 23.3] + go-version: [1.21.x, 1.22.x] + 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 e58998c24..b373a0322 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -64,6 +64,12 @@ linters-settings: # put imports beginning with prefix after 3rd-party packages; # it's a comma-separated list of prefixes local-prefixes: github.com/ydb-platform/ydb-go-sdk/v3 + gci: + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - prefix(github.com/ydb-platform/ydb-go-sdk/v3) # Custom section: groups all imports with the specified Prefix. + skip-generated: true goconst: # minimal length of string constant, 3 by default min-len: 2 @@ -106,6 +112,10 @@ linters-settings: # if it's called for subdir of a project it can't find external interfaces. All text editor integrations # with golangci-lint call it on a directory with the changed file. check-exported: false + gomoddirectives: + replace-local: true + replace-allow-list: + - xorm.io/xorm gocritic: disabled-checks: - whyNoLint # https://github.com/go-critic/go-critic/issues/1063 @@ -213,32 +223,24 @@ linters: - cyclop - depguard - dupl - - errname - exhaustive - exhaustivestruct - exhaustruct - forbidigo - forcetypeassert - funlen - - gci - gochecknoglobals - gocognit - godot - goerr113 - golint - gomnd - - gomoddirectives - ifshort - interfacebloat - - interfacer - ireturn - maintidx - - maligned - - nilerr - - nlreturn - nonamedreturns - paralleltest - - protogetter - scopelint - structcheck - testableexamples diff --git a/CHANGELOG.md b/CHANGELOG.md index 5230596b9..f8e848965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,98 @@ +## 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) + +## v3.56.0 +* Fixed handle of operational errors in topic streams +* The minimum version of Go in `ydb-go-sdk` has been raised to `go1.21` +* Fixed topic writer infinite reconnections in some cases +* Refactored nil on err `internal/grpcwrapper/rawydb/issues.go`, when golangci-lint nilerr enabled +* Refactored nil on err `internal/grpcwrapper/rawtopic/describe_topic.go`, when golangci-lint nilerr enabled + +## v3.55.3 +* Fixed handle of operational errors in topic streams (backported fix only) + +## v3.55.2 +* Fixed init info in topic writer, when autoseq num turned off. + +## v3.55.1 +* Supported column name prefix `__discard_column_` for discard columns in result sets +* Made `StatusIds_SESSION_EXPIRED` retriable for idempotent operations + ## v3.55.0 * Refactored `internal/value/intervalValue.Yql()` * The minimum version of Go in `ydb-go-sdk` has been raised to `go1.20` @@ -12,14 +107,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 @@ -43,7 +138,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 @@ -192,7 +287,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 @@ [![WebSite](https://img.shields.io/badge/website-ydb.tech-blue.svg)](https://ydb.tech) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](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/balancers.go b/balancers/balancers.go index f5fa1076e..c73c8f503 100644 --- a/balancers/balancers.go +++ b/balancers/balancers.go @@ -40,6 +40,7 @@ func (filterLocalDC) String() string { func PreferLocalDC(balancer *balancerConfig.Config) *balancerConfig.Config { balancer.Filter = filterLocalDC{} balancer.DetectLocalDC = true + return balancer } @@ -49,6 +50,7 @@ func PreferLocalDC(balancer *balancerConfig.Config) *balancerConfig.Config { func PreferLocalDCWithFallBack(balancer *balancerConfig.Config) *balancerConfig.Config { balancer = PreferLocalDC(balancer) balancer.AllowFallback = true + return balancer } @@ -61,6 +63,7 @@ func (locations filterLocations) Allow(_ balancerConfig.Info, c conn.Conn) bool return true } } + return false } @@ -91,6 +94,7 @@ func PreferLocations(balancer *balancerConfig.Config, locations ...string) *bala } sort.Strings(locations) balancer.Filter = filterLocations(locations) + return balancer } @@ -100,6 +104,7 @@ func PreferLocations(balancer *balancerConfig.Config, locations ...string) *bala func PreferLocationsWithFallback(balancer *balancerConfig.Config, locations ...string) *balancerConfig.Config { balancer = PreferLocations(balancer, locations...) balancer.AllowFallback = true + return balancer } @@ -129,6 +134,7 @@ func Prefer(balancer *balancerConfig.Config, filter func(endpoint Endpoint) bool balancer.Filter = filterFunc(func(_ balancerConfig.Info, c conn.Conn) bool { return filter(c.Endpoint()) }) + return balancer } @@ -138,6 +144,7 @@ func Prefer(balancer *balancerConfig.Config, filter func(endpoint Endpoint) bool func PreferWithFallback(balancer *balancerConfig.Config, filter func(endpoint Endpoint) bool) *balancerConfig.Config { balancer = Prefer(balancer, filter) balancer.AllowFallback = true + return balancer } diff --git a/balancers/balancers_test.go b/balancers/balancers_test.go index 2c7c4efd8..75d0758b0 100644 --- a/balancers/balancers_test.go +++ b/balancers/balancers_test.go @@ -66,5 +66,6 @@ func applyPreferFilter(info balancerConfig.Info, b *balancerConfig.Config, conns res = append(res, c) } } + return res } diff --git a/balancers/config.go b/balancers/config.go index 04623862f..8bc38199c 100644 --- a/balancers/config.go +++ b/balancers/config.go @@ -92,6 +92,7 @@ func CreateFromConfig(s string) (*balancerConfig.Config, error) { if c.Fallback { return PreferLocalDCWithFallBack(b), nil } + return PreferLocalDC(b), nil case preferTypeLocations: if len(c.Locations) == 0 { @@ -100,6 +101,7 @@ func CreateFromConfig(s string) (*balancerConfig.Config, error) { if c.Fallback { return PreferLocationsWithFallback(b, c.Locations...), nil } + return PreferLocations(b, c.Locations...), nil default: return b, nil @@ -114,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) } } @@ -125,6 +127,7 @@ func FromConfig(config string, opts ...fromConfigOption) *balancerConfig.Config if h.errorHandler != nil { h.errorHandler(err) } + return h.fallbackBalancer } diff --git a/config/config.go b/config/config.go index 5cf9a0a09..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( @@ -292,5 +302,6 @@ func (c *Config) With(opts ...Option) *Config { c.trace, c.metaOptions..., ) + return c } diff --git a/config/defaults.go b/config/defaults.go index a5dcfeb83..e63867808 100644 --- a/config/defaults.go +++ b/config/defaults.go @@ -64,6 +64,7 @@ func defaultGrpcOptions(t *trace.Driver, secure bool, tlsConfig *tls.Config) (op insecure.NewCredentials(), )) } + return opts } @@ -72,6 +73,7 @@ func certPool() *x509.CertPool { if err == nil { return certPool } + return x509.NewCertPool() } 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/example_test.go b/coordination/example_test.go index f1e416294..57657d779 100644 --- a/coordination/example_test.go +++ b/coordination/example_test.go @@ -14,6 +14,7 @@ func Example() { 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 @@ -28,12 +29,14 @@ func Example() { }) if err != nil { fmt.Printf("failed to create node: %v", err) + return } defer db.Coordination().DropNode(ctx, "/local/test") e, c, err := db.Coordination().DescribeNode(ctx, "/local/test") if err != nil { fmt.Printf("failed to describe node: %v", err) + return } fmt.Printf("node description: %+v\nnode config: %+v\n", e, c) diff --git a/discovery/example_test.go b/discovery/example_test.go index 30ca45b40..87b142ab1 100644 --- a/discovery/example_test.go +++ b/discovery/example_test.go @@ -12,12 +12,14 @@ func Example_discoverCluster() { 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 endpoints, err := db.Discovery().Discover(ctx) if err != nil { fmt.Printf("discover failed: %v", err) + return } fmt.Printf("%s endpoints:\n", db.Name()) @@ -31,12 +33,14 @@ func Example_whoAmI() { 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 whoAmI, err := db.Discovery().WhoAmI(ctx) if err != nil { fmt.Printf("discover failed: %v", err) + return } fmt.Printf("%s whoAmI: %s\n", db.Name(), whoAmI.String()) 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/driver_string.go b/driver_string.go index d2b5fd6f7..32b52fbc8 100644 --- a/driver_string.go +++ b/driver_string.go @@ -18,5 +18,6 @@ func (d *Driver) String() string { fmt.Fprintf(buffer, ",Credentials:%v", c.String()) } buffer.WriteByte('}') + return buffer.String() } diff --git a/driver_string_test.go b/driver_string_test.go index 812a81e94..cc4e1cbe9 100644 --- a/driver_string_test.go +++ b/driver_string_test.go @@ -23,7 +23,7 @@ func TestDriver_String(t *testing.T) { config.WithDatabase("local"), config.WithSecure(false), )}, - s: `Driver{Endpoint:"localhost",Database:"local",Secure:false,Credentials:Anonymous{From:"github.com/ydb-platform/ydb-go-sdk/v3/config.defaultConfig(defaults.go:88)"}}`, //nolint:lll + s: `Driver{Endpoint:"localhost",Database:"local",Secure:false,Credentials:Anonymous{From:"github.com/ydb-platform/ydb-go-sdk/v3/config.defaultConfig(defaults.go:90)"}}`, //nolint:lll }, { name: xtest.CurrentFileLine(), @@ -32,7 +32,7 @@ func TestDriver_String(t *testing.T) { config.WithDatabase("local"), config.WithSecure(true), )}, - s: `Driver{Endpoint:"localhost",Database:"local",Secure:true,Credentials:Anonymous{From:"github.com/ydb-platform/ydb-go-sdk/v3/config.defaultConfig(defaults.go:88)"}}`, //nolint:lll + s: `Driver{Endpoint:"localhost",Database:"local",Secure:true,Credentials:Anonymous{From:"github.com/ydb-platform/ydb-go-sdk/v3/config.defaultConfig(defaults.go:90)"}}`, //nolint:lll }, { name: xtest.CurrentFileLine(), 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/auth/environ/main.go b/examples/auth/environ/main.go index 86dd1b01c..0ab5d2228 100644 --- a/examples/auth/environ/main.go +++ b/examples/auth/environ/main.go @@ -6,7 +6,6 @@ import ( "os" environ "github.com/ydb-platform/ydb-go-sdk-auth-environ" - ydb "github.com/ydb-platform/ydb-go-sdk/v3" ) diff --git a/examples/auth/metadata_credentials/main.go b/examples/auth/metadata_credentials/main.go index 00e55ffc2..073025b53 100644 --- a/examples/auth/metadata_credentials/main.go +++ b/examples/auth/metadata_credentials/main.go @@ -6,9 +6,8 @@ import ( "fmt" "os" - yc "github.com/ydb-platform/ydb-go-yc" - ydb "github.com/ydb-platform/ydb-go-sdk/v3" + yc "github.com/ydb-platform/ydb-go-yc" ) var dsn string diff --git a/examples/auth/service_account_credentials/main.go b/examples/auth/service_account_credentials/main.go index 879ea9ba7..160d488b8 100644 --- a/examples/auth/service_account_credentials/main.go +++ b/examples/auth/service_account_credentials/main.go @@ -6,9 +6,8 @@ import ( "fmt" "os" - yc "github.com/ydb-platform/ydb-go-yc" - ydb "github.com/ydb-platform/ydb-go-sdk/v3" + yc "github.com/ydb-platform/ydb-go-yc" ) var ( diff --git a/examples/basic/database_sql/data.go b/examples/basic/database_sql/data.go index 279c895be..5b373b7a3 100644 --- a/examples/basic/database_sql/data.go +++ b/examples/basic/database_sql/data.go @@ -8,7 +8,6 @@ import ( "time" "github.com/google/uuid" - "github.com/ydb-platform/ydb-go-sdk/v3/table/types" ) @@ -19,6 +18,7 @@ func seriesData(id string, released time.Time, title, info, comment string) type } else { commentv = types.OptionalValue(types.TextValue(comment)) } + return types.StructValue( types.StructFieldValue("series_id", types.BytesValueFromString(id)), types.StructFieldValue("release_date", types.DateValueFromTime(released)), @@ -60,6 +60,7 @@ func getData() (series, seasons, episodes []types.Value) { seasons = append(seasons, seasonsData...) episodes = append(episodes, episodesData...) } + return } @@ -115,6 +116,7 @@ func getDataForITCrowd(seriesID string) (series types.Value, seasons, episodes [ episodes = append(episodes, episodeData(seriesID, seasonID, uuid.New().String(), title, date)) } } + return series, seasons, episodes } @@ -194,6 +196,7 @@ func getDataForSiliconValley(seriesID string) (series types.Value, seasons, epis episodes = append(episodes, episodeData(seriesID, seasonID, uuid.New().String(), title, date)) } } + return series, seasons, episodes } @@ -204,5 +207,6 @@ func date(date string) time.Time { if err != nil { panic(err) } + return t } diff --git a/examples/basic/database_sql/main.go b/examples/basic/database_sql/main.go index 0c44ce7b9..e4c31bfe9 100644 --- a/examples/basic/database_sql/main.go +++ b/examples/basic/database_sql/main.go @@ -9,7 +9,6 @@ import ( "time" environ "github.com/ydb-platform/ydb-go-sdk-auth-environ" - "github.com/ydb-platform/ydb-go-sdk/v3" "github.com/ydb-platform/ydb-go-sdk/v3/sugar" ) diff --git a/examples/basic/database_sql/series.go b/examples/basic/database_sql/series.go index d76904e50..28c39a50d 100644 --- a/examples/basic/database_sql/series.go +++ b/examples/basic/database_sql/series.go @@ -30,6 +30,7 @@ func selectDefault(ctx context.Context, db *sql.DB) (err error) { return err } log.Printf("AST = %s\n\nPlan = %s", ast, plan) + return nil }, retry.WithIdempotent(true)) if err != nil { @@ -61,11 +62,13 @@ func selectDefault(ctx context.Context, db *sql.DB) (err error) { *id, *title, releaseDate.Format("2006-01-02"), ) } + return rows.Err() }, retry.WithIdempotent(true)) if err != nil { return fmt.Errorf("execute data query failed: %w", err) } + return nil } @@ -154,11 +157,13 @@ func selectScan(ctx context.Context, db *sql.DB) (err error) { episodeID, title, firstAired.Format("2006-01-02"), ) } + return rows.Err() }, retry.WithIdempotent(true)) if err != nil { return fmt.Errorf("scan query failed: %w", err) } + return nil } @@ -201,11 +206,13 @@ func fillTablesWithData(ctx context.Context, db *sql.DB) (err error) { ); err != nil { return err } + return nil }, retry.WithIdempotent(true)) if err != nil { return fmt.Errorf("upsert query failed: %w", err) } + return nil } @@ -232,8 +239,10 @@ func prepareSchema(ctx context.Context, db *sql.DB) (err error) { ) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "create series table failed: %v", err) + return err } + return nil }, retry.WithIdempotent(true)) if err != nil { @@ -263,8 +272,10 @@ func prepareSchema(ctx context.Context, db *sql.DB) (err error) { ) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "create seasons table failed: %v\n", err) + return err } + return nil }, retry.WithIdempotent(true)) if err != nil { @@ -296,13 +307,16 @@ func prepareSchema(ctx context.Context, db *sql.DB) (err error) { ) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "create episodes table failed: %v\n", err) + return err } + return nil }, retry.WithIdempotent(true)) if err != nil { return fmt.Errorf("create table failed: %w", err) } + return nil } @@ -326,5 +340,6 @@ func dropTableIfExists(ctx context.Context, cc *sql.Conn, tableName string) erro if err != nil { return fmt.Errorf("drop table failed: %w", err) } + return nil } diff --git a/examples/basic/gorm/data.go b/examples/basic/gorm/data.go index 6d92ad170..b5b72f269 100644 --- a/examples/basic/gorm/data.go +++ b/examples/basic/gorm/data.go @@ -373,5 +373,6 @@ func date(date string) time.Time { if err != nil { panic(err) } + return t } diff --git a/examples/basic/gorm/main.go b/examples/basic/gorm/main.go index 6affba9e0..725032216 100644 --- a/examples/basic/gorm/main.go +++ b/examples/basic/gorm/main.go @@ -85,6 +85,7 @@ func prepareScheme(db *gorm.DB) error { ); err != nil { return err } + return db.AutoMigrate( &Series{}, &Season{}, @@ -123,6 +124,7 @@ func readAll(db *gorm.DB) error { } } } + return nil } @@ -161,5 +163,6 @@ func findEpisodesByTitle(db *gorm.DB, fragment string) error { episodes[i].ID, episodes[i].AirDate.Format(dateISO8601), episodes[i].Title, ) } + return nil } diff --git a/examples/basic/gorm/models.go b/examples/basic/gorm/models.go index c96edc7ee..8b2b05d64 100644 --- a/examples/basic/gorm/models.go +++ b/examples/basic/gorm/models.go @@ -26,6 +26,7 @@ func (s *Series) BeforeCreate(_ *gorm.DB) (err error) { for i := range s.Seasons { s.Seasons[i].SeriesID = s.ID } + return } @@ -48,6 +49,7 @@ func (s *Season) BeforeCreate(_ *gorm.DB) (err error) { for i := range s.Episodes { s.Episodes[i].SeasonID = s.ID } + return } @@ -64,5 +66,6 @@ func (e *Episode) BeforeCreate(_ *gorm.DB) (err error) { return err } e.ID = id.String() + return } 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 99% rename from examples/basic/native/data.go rename to examples/basic/native/table/data.go index cc8e7f3fc..cc4b12547 100644 --- a/examples/basic/native/data.go +++ b/examples/basic/native/table/data.go @@ -13,6 +13,7 @@ func seriesData(id uint64, released time.Time, title, info, comment string) type } else { commentv = types.OptionalValue(types.TextValue(comment)) } + return types.StructValue( types.StructFieldValue("series_id", types.Uint64Value(id)), types.StructFieldValue("release_date", types.DateValueFromTime(released)), @@ -155,5 +156,6 @@ func days(date string) time.Time { if err != nil { panic(err) } + return t } 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 e2cfd23df..b624c4f12 100644 --- a/examples/basic/native/main.go +++ b/examples/basic/native/table/main.go @@ -8,7 +8,6 @@ import ( "time" environ "github.com/ydb-platform/ydb-go-sdk-auth-environ" - "github.com/ydb-platform/ydb-go-sdk/v3" "github.com/ydb-platform/ydb-go-sdk/v3/sugar" ) @@ -31,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 a9e267aab..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,126 +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) - return - }, - ) - if err != nil { - return err - } - log.Println("> describe_table_options:") + desc, err := s.DescribeTableOptions(ctx) + if err != nil { + return err + } - 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], - ) - } + 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], + ) + } - 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 }}"); @@ -218,51 +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 ) - } - } - return res.Err() + 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() + }, + ) } -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 }}"); @@ -317,19 +317,21 @@ func scanQuerySelect(ctx context.Context, c table.Client, prefix string) (err er log.Printf("# Season, SeriesId: %d, SeasonId: %d, Title: %s, Air date: %s", seriesID, seasonID, title, date) } } + return res.Err() }, ) } -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, @@ -338,16 +340,16 @@ func fillTablesWithData(ctx context.Context, c table.Client, prefix string) (err table.ValueParam("$seasonsData", getSeasonsData()), table.ValueParam("$episodesData", getEpisodesData()), )) + 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)), @@ -355,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)), @@ -371,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)), @@ -387,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 { @@ -407,10 +401,10 @@ func describeTable(ctx context.Context, c table.Client, path string) (err error) for i := range desc.Columns { log.Printf("column, name: %s, %s", desc.Columns[i].Type, desc.Columns[i].Name) } + return nil }, ) - return } func render(t *template.Template, data interface{}) string { @@ -419,5 +413,6 @@ func render(t *template.Template, data interface{}) string { if err != nil { panic(err) } + return buf.String() } diff --git a/examples/basic/xorm/data.go b/examples/basic/xorm/data.go index eb085e19c..c6cfbb3fe 100644 --- a/examples/basic/xorm/data.go +++ b/examples/basic/xorm/data.go @@ -49,6 +49,7 @@ func getData() (series []*Series, seasons []*Seasons, episodes []*Episodes) { seasons = append(seasons, seasonsData...) episodes = append(episodes, episodesData...) } + return } @@ -112,6 +113,7 @@ func getDataForITCrowd(seriesID string) (series *Series, seasons []*Seasons, epi episodes = append(episodes, episodeData(seasonID, uuid.New().String(), title, date)) } } + return series, seasons, episodes } @@ -201,6 +203,7 @@ func getDataForSiliconValley(seriesID string) (series *Series, seasons []*Season episodes = append(episodes, episodeData(seasonID, uuid.New().String(), title, date)) } } + return series, seasons, episodes } @@ -211,5 +214,6 @@ func date(date string) time.Time { if err != nil { panic(err) } + return t } diff --git a/examples/basic/xorm/main.go b/examples/basic/xorm/main.go index be8f8cbcc..8a0961af5 100644 --- a/examples/basic/xorm/main.go +++ b/examples/basic/xorm/main.go @@ -7,12 +7,11 @@ import ( "time" _ "github.com/lib/pq" + _ "github.com/ydb-platform/ydb-go-sdk/v3" _ "modernc.org/sqlite" "xorm.io/builder" "xorm.io/xorm" xormLog "xorm.io/xorm/log" - - _ "github.com/ydb-platform/ydb-go-sdk/v3" ) var envNotFoundMessage = `DSN environment variable not defined @@ -77,6 +76,7 @@ func prepareScheme(db *xorm.Engine) error { } err = db.CreateTables(&Series{}, &Seasons{}, &Episodes{}) + return err } @@ -89,6 +89,7 @@ func fillData(db *xorm.Engine) error { if _, err := session.Insert(&series, &seasons, &episodes); err != nil { return err } + return nil } @@ -178,5 +179,6 @@ func findEpisodesByTitle(db *xorm.Engine, fragment string) error { e.ID, e.AirDate.Format(dateISO8601), e.Title, ) } + return nil } diff --git a/examples/ddl/ddl.go b/examples/ddl/ddl.go index 6641c913f..9e0d4725a 100644 --- a/examples/ddl/ddl.go +++ b/examples/ddl/ddl.go @@ -71,11 +71,13 @@ func executeQuery(ctx context.Context, c table.Client, prefix, query string) (er err = c.Do(ctx, func(ctx context.Context, s table.Session) error { err = s.ExecuteSchemeQuery(ctx, fmt.Sprintf(query, prefix)) + return err }, ) if err != nil { return err } + return nil } diff --git a/examples/ddl/main.go b/examples/ddl/main.go index 75bd8576d..6e219b001 100644 --- a/examples/ddl/main.go +++ b/examples/ddl/main.go @@ -8,7 +8,6 @@ import ( "path" environ "github.com/ydb-platform/ydb-go-sdk-auth-environ" - ydb "github.com/ydb-platform/ydb-go-sdk/v3" ) diff --git a/examples/decimal/decimal.go b/examples/decimal/decimal.go index 9934ca15e..51400f575 100644 --- a/examples/decimal/decimal.go +++ b/examples/decimal/decimal.go @@ -34,5 +34,6 @@ func render(t *template.Template, data interface{}) string { if err != nil { panic(err) } + return buf.String() } diff --git a/examples/decimal/main.go b/examples/decimal/main.go index 1662dc983..8e77c2ce1 100644 --- a/examples/decimal/main.go +++ b/examples/decimal/main.go @@ -9,7 +9,6 @@ import ( "path" 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/table" "github.com/ydb-platform/ydb-go-sdk/v3/table/options" @@ -131,6 +130,7 @@ func main() { fmt.Println(p.String()) } } + return res.Err() }, ) diff --git a/examples/describe/main.go b/examples/describe/main.go index cc8f7106c..93fb00333 100644 --- a/examples/describe/main.go +++ b/examples/describe/main.go @@ -11,7 +11,6 @@ import ( "text/template" 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/retry" "github.com/ydb-platform/ydb-go-sdk/v3/scheme" @@ -102,10 +101,12 @@ func list(ctx context.Context, db *ydb.Driver, t *template.Template, p string) { var err error err = retry.Retry(ctx, func(ctx context.Context) (err error) { dir, err = db.Scheme().ListDirectory(ctx, p) + return err }, retry.WithIdempotent(true)) if err != nil { fmt.Printf("list directory '%s' failed: %v\n", p, err) + return } @@ -122,10 +123,12 @@ func list(ctx context.Context, db *ydb.Driver, t *template.Template, p string) { var desc options.Description err = db.Table().Do(ctx, func(ctx context.Context, s table.Session) (err error) { desc, err = s.DescribeTable(ctx, pt) + return err }, table.WithIdempotent()) if err != nil { fmt.Printf("describe '%s' failed: %v\n", pt, err) + continue } desc.Name = pt diff --git a/examples/go.mod b/examples/go.mod index 141a8779d..4b8c940bb 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -1,6 +1,6 @@ module examples -go 1.20 +go 1.21 require ( github.com/google/uuid v1.3.0 @@ -9,8 +9,8 @@ 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 google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 gorm.io/driver/postgres v1.5.0 @@ -32,7 +32,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 @@ -49,11 +50,10 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect 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-20231215113745-46f6d30f974a // indirect - github.com/ydb-platform/ydb-go-sdk-metrics v0.16.3 // indirect + github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf // 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.9.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 @@ -62,7 +62,7 @@ require ( 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 57cee1bb6..79278f3b3 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -770,7 +770,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -945,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= @@ -1098,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= @@ -1127,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= @@ -1195,14 +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-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-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= @@ -1239,6 +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.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= @@ -1265,6 +1267,7 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/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.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/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= @@ -1323,10 +1326,12 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +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= @@ -1387,6 +1392,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= @@ -1537,6 +1543,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1550,6 +1557,7 @@ golang.org/x/sys v0.15.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= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= @@ -1650,6 +1658,7 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= @@ -1945,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/pagination/cities.go b/examples/pagination/cities.go index 2fd8aa846..620e9ee72 100644 --- a/examples/pagination/cities.go +++ b/examples/pagination/cities.go @@ -69,6 +69,7 @@ func selectPaging( }() if !res.NextResultSet(ctx) || !res.HasNextRow() { empty = true + return res.Err() } var addr string @@ -83,9 +84,11 @@ func selectPaging( } fmt.Printf("\t%v, School #%v, Address: %v\n", *lastCity, *lastNum, addr) } + return res.Err() }, ) + return empty, err } @@ -112,8 +115,10 @@ func fillTableWithData(ctx context.Context, c table.Client, prefix string) (err _, _, err = s.Execute(ctx, writeTx, query, table.NewQueryParameters( table.ValueParam("$schoolsData", getSchoolData()), )) + return err }) + return err } diff --git a/examples/pagination/main.go b/examples/pagination/main.go index 4510f1f3a..24690610b 100644 --- a/examples/pagination/main.go +++ b/examples/pagination/main.go @@ -8,7 +8,6 @@ import ( "path" 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" ) diff --git a/examples/read_table/main.go b/examples/read_table/main.go index 931672e6f..2e72d944a 100644 --- a/examples/read_table/main.go +++ b/examples/read_table/main.go @@ -9,7 +9,6 @@ import ( "path" 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/table/options" "github.com/ydb-platform/ydb-go-sdk/v3/table/types" diff --git a/examples/read_table/orders.go b/examples/read_table/orders.go index 7255608ef..c9e007193 100644 --- a/examples/read_table/orders.go +++ b/examples/read_table/orders.go @@ -31,6 +31,7 @@ func dropTableIfExists(ctx context.Context, c table.Client, path string) (err er if !ydb.IsOperationErrorSchemeError(err) { return err } + return nil } @@ -76,6 +77,7 @@ func render(t *template.Template, data interface{}) string { if err != nil { panic(err) } + return buf.String() } @@ -116,6 +118,7 @@ func fillTable(ctx context.Context, c table.Client, prefix string) (err error) { ), ), ) + return err }, ) @@ -126,6 +129,7 @@ func order(customerID, orderID uint64, description, date string) types.Value { if err != nil { panic(err) } + return types.StructValue( types.StructFieldValue("customer_id", types.Uint64Value(customerID)), types.StructFieldValue("order_id", types.Uint64Value(orderID)), @@ -173,8 +177,10 @@ func readTable(ctx context.Context, c table.Client, path string, opts ...options } } } + return res.Err() }, ) + return err } diff --git a/examples/serverless/healthcheck/main.go b/examples/serverless/healthcheck/main.go index fa44c3b72..5e828aa31 100644 --- a/examples/serverless/healthcheck/main.go +++ b/examples/serverless/healthcheck/main.go @@ -32,6 +32,7 @@ func (u *URLs) String() string { // Set appends new value to URLs holder func (u *URLs) Set(s string) error { u.urls = append(u.urls, s) + return nil } diff --git a/examples/serverless/healthcheck/service.go b/examples/serverless/healthcheck/service.go index c56953407..7e5ecb486 100644 --- a/examples/serverless/healthcheck/service.go +++ b/examples/serverless/healthcheck/service.go @@ -14,7 +14,6 @@ import ( "time" environ "github.com/ydb-platform/ydb-go-sdk-auth-environ" - "github.com/ydb-platform/ydb-go-sdk/v3" "github.com/ydb-platform/ydb-go-sdk/v3/sugar" "github.com/ydb-platform/ydb-go-sdk/v3/table" @@ -43,6 +42,7 @@ func getService(ctx context.Context, dsn string, opts ...ydb.Option) (s *service s.db, err = ydb.Open(ctx, dsn, opts...) if err != nil { err = fmt.Errorf("connect error: %w", err) + return } err = s.createTableIfNotExists(ctx) @@ -53,8 +53,10 @@ func getService(ctx context.Context, dsn string, opts ...ydb.Option) (s *service }) if err != nil { once = sync.Once{} + return nil, err } + return s, nil } @@ -83,6 +85,7 @@ func (s *service) createTableIfNotExists(ctx context.Context) error { AUTO_PARTITIONING_BY_LOAD = ENABLED );`, path.Join(s.db.Name(), prefix), ) + return s.db.Table().Do(ctx, func(ctx context.Context, s table.Session) error { return s.ExecuteSchemeQuery(ctx, query) @@ -109,6 +112,7 @@ func (s *service) ping(ctx context.Context, path string) (code int32, err error) defer func() { _ = response.Body.Close() }() + return int32(response.StatusCode), nil } @@ -160,6 +164,7 @@ func (s *service) upsertRows(ctx context.Context, rows []row) (err error) { if err != nil { return err.Error() } + return "" }(rows[i].err))), ) @@ -186,12 +191,14 @@ func (s *service) upsertRows(ctx context.Context, rows []row) (err error) { table.ValueParam("$rows", types.ListValue(values...)), ), ) + return err }, ) if err != nil { return fmt.Errorf("error on upsert rows: %w", err) } + return nil } @@ -209,5 +216,6 @@ func Serverless(ctx context.Context) error { return fmt.Errorf("error on create service: %w", err) } defer s.Close(ctx) + return s.check(ctx, strings.Split(os.Getenv("URLS"), ",")) } diff --git a/examples/serverless/url_shortener/main.go b/examples/serverless/url_shortener/main.go index 40eadc06a..7e8ad0919 100644 --- a/examples/serverless/url_shortener/main.go +++ b/examples/serverless/url_shortener/main.go @@ -93,6 +93,7 @@ func main() { if err != nil { fmt.Println() fmt.Println("Create service failed. Re-run with flag '-log-level=warn' and see logs") + return } defer s.Close(ctx) diff --git a/examples/serverless/url_shortener/service.go b/examples/serverless/url_shortener/service.go index 528549ce6..041c55d9d 100644 --- a/examples/serverless/url_shortener/service.go +++ b/examples/serverless/url_shortener/service.go @@ -21,8 +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" @@ -46,6 +45,7 @@ func hash(s string) (string, error) { if err != nil { return "", err } + return hex.EncodeToString(hasher.Sum(nil)), nil } @@ -63,6 +63,7 @@ func render(t *template.Template, data interface{}) string { if err != nil { panic(err) } + return buf.String() } @@ -149,6 +150,7 @@ func getService(ctx context.Context, dsn string, opts ...ydb.Option) (s *service s.db, err = ydb.Open(ctx, dsn, opts...) if err != nil { err = fmt.Errorf("connect error: %w", err) + return } @@ -163,13 +165,16 @@ func getService(ctx context.Context, dsn string, opts ...ydb.Option) (s *service if err != nil { _ = s.db.Close(ctx) err = fmt.Errorf("error on create table: %w", err) + return } }) if err != nil { once = sync.Once{} + return nil, err } + return s, nil } @@ -193,9 +198,11 @@ func (s *service) createTable(ctx context.Context) (err error) { TablePathPrefix: path.Join(s.db.Name(), prefix), }, ) + return s.db.Table().Do(ctx, func(ctx context.Context, s table.Session) error { err := s.ExecuteSchemeQuery(ctx, query) + return err }, ) @@ -237,9 +244,11 @@ func (s *service) insertShort(ctx context.Context, url string) (h string, err er ), options.WithCollectStatsModeBasic(), ) + return }, ) + return h, err } @@ -276,6 +285,7 @@ func (s *service) selectLong(ctx context.Context, hash string) (url string, err ), options.WithCollectStatsModeBasic(), ) + return err }, ) @@ -291,9 +301,11 @@ func (s *service) selectLong(ctx context.Context, hash string) (url string, err err = res.ScanNamed( named.OptionalWithDefault("src", &src), ) + return src, err } } + return "", fmt.Errorf("hash '%s' is not found", hash) } @@ -306,6 +318,7 @@ func successToString(b bool) string { if b { return "true" } + return "false" } @@ -333,6 +346,7 @@ func (s *service) handleIndex(w http.ResponseWriter, r *http.Request) { tpl, err = template.ParseFS(static, "static/index.html") if err != nil { writeResponse(w, http.StatusInternalServerError, err.Error()) + return } w.Header().Set("Content-Type", "text/html") @@ -342,6 +356,7 @@ func (s *service) handleIndex(w http.ResponseWriter, r *http.Request) { } if err = tpl.Execute(w, data); err != nil { writeResponse(w, http.StatusInternalServerError, err.Error()) + return } } @@ -371,16 +386,19 @@ func (s *service) handleShorten(w http.ResponseWriter, r *http.Request) { url, err = io.ReadAll(r.Body) if err != nil { writeResponse(w, http.StatusInternalServerError, err.Error()) + return } if !isLongCorrect(string(url)) { err = fmt.Errorf("'%s' is not a valid URL", url) writeResponse(w, http.StatusBadRequest, err.Error()) + return } hash, err = s.insertShort(r.Context(), string(url)) if err != nil { writeResponse(w, http.StatusInternalServerError, err.Error()) + return } w.Header().Set("Content-Type", "application/text") @@ -412,11 +430,13 @@ func (s *service) handleLonger(w http.ResponseWriter, r *http.Request) { if !isShortCorrect(path[len(path)-1]) { err = fmt.Errorf("'%s' is not a valid short path", path[len(path)-1]) writeResponse(w, http.StatusBadRequest, err.Error()) + return } url, err = s.selectLong(r.Context(), path[len(path)-1]) if err != nil { writeResponse(w, http.StatusInternalServerError, err.Error()) + return } http.Redirect(w, r, url, http.StatusSeeOther) @@ -433,6 +453,7 @@ func Serverless(w http.ResponseWriter, r *http.Request) { ) if err != nil { writeResponse(w, http.StatusInternalServerError, err.Error()) + return } defer s.Close(r.Context()) diff --git a/examples/topic/cdc-cache-bus-freeseats/cache.go b/examples/topic/cdc-cache-bus-freeseats/cache.go index ada1aac22..4e67084f6 100644 --- a/examples/topic/cdc-cache-bus-freeseats/cache.go +++ b/examples/topic/cdc-cache-bus-freeseats/cache.go @@ -35,6 +35,7 @@ func (c *Cache) Get(key string) (int64, bool) { if time.Now().After(item.ExpiresAt) { delete(c.values, key) + return 0, false } diff --git a/examples/topic/cdc-cache-bus-freeseats/database.go b/examples/topic/cdc-cache-bus-freeseats/database.go index 218b892b6..43ad188ce 100644 --- a/examples/topic/cdc-cache-bus-freeseats/database.go +++ b/examples/topic/cdc-cache-bus-freeseats/database.go @@ -32,6 +32,7 @@ func createTables(ctx context.Context, db *ydb.Driver) error { if ydb.IsOperationErrorSchemeError(err) { err = nil } + return err }) if err != nil { @@ -59,6 +60,7 @@ UPSERT INTO bus (id, freeSeats) VALUES ("bus1", 40), ("bus2", 60); if err != nil { return fmt.Errorf("failed insert rows: %w", err) } + return nil } @@ -104,6 +106,7 @@ func connect() *ydb.Driver { panic(fmt.Errorf("failed to create to ydb: %w", err)) } log.Printf("connected to database") + return db } diff --git a/examples/topic/cdc-cache-bus-freeseats/webserver.go b/examples/topic/cdc-cache-bus-freeseats/webserver.go index 790339b53..3825fabce 100644 --- a/examples/topic/cdc-cache-bus-freeseats/webserver.go +++ b/examples/topic/cdc-cache-bus-freeseats/webserver.go @@ -55,6 +55,7 @@ func (s *server) GetFreeSeatsHandler(writer http.ResponseWriter, request *http.R freeSeats, err := s.getFreeSeats(ctx, id) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) + return } duration := time.Since(start) @@ -73,6 +74,7 @@ func (s *server) BuyTicketHandler(writer http.ResponseWriter, request *http.Requ } else { http.Error(writer, err.Error(), http.StatusInternalServerError) } + return } //nolint:gocritic @@ -81,7 +83,7 @@ func (s *server) BuyTicketHandler(writer http.ResponseWriter, request *http.Requ s.writeAnswer(writer, freeSeats, duration) } -func (s *server) writeAnswer(writer http.ResponseWriter, freeSeats int64, duration time.Duration) { +func (s *server) writeAnswer(writer io.Writer, freeSeats int64, duration time.Duration) { _, _ = fmt.Fprintf(writer, "%v\n\nDuration: %v\n", freeSeats, duration) } @@ -105,6 +107,7 @@ func (s *server) getContentFromDB(ctx context.Context, id string) (int64, error) err := s.db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { var err error freeSeats, err = s.getFreeSeatsTx(ctx, tx, id) + return err }) @@ -129,6 +132,7 @@ SELECT freeSeats FROM bus WHERE id=$id; if !res.NextRow() { freeSeats = 0 + return 0, errors.New("not found") } @@ -157,11 +161,13 @@ DECLARE $id AS Text; UPDATE bus SET freeSeats = freeSeats - 1 WHERE id=$id; `, table.NewQueryParameters(table.ValueParam("$id", types.UTF8Value(id)))) + return err }) if err == nil { freeSeats-- } + return freeSeats, err } @@ -192,6 +198,7 @@ func (s *server) IndexPageHandler(writer http.ResponseWriter, request *http.Requ }) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) + return } diff --git a/examples/topic/cdc-fill-and-read/main.go b/examples/topic/cdc-fill-and-read/main.go index b4ebf7c95..d0cfdbe47 100644 --- a/examples/topic/cdc-fill-and-read/main.go +++ b/examples/topic/cdc-fill-and-read/main.go @@ -10,7 +10,6 @@ import ( "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/topic/topicoptions" "github.com/ydb-platform/ydb-go-sdk/v3/topic/topictypes" diff --git a/examples/topic/cdc-fill-and-read/tables.go b/examples/topic/cdc-fill-and-read/tables.go index fcf5050f5..a522bdf11 100644 --- a/examples/topic/cdc-fill-and-read/tables.go +++ b/examples/topic/cdc-fill-and-read/tables.go @@ -29,6 +29,7 @@ func dropTableIfExists(ctx context.Context, c table.Client, path string) (err er if !ydb.IsOperationErrorSchemeError(err) { return err } + return nil } @@ -60,11 +61,13 @@ WITH ( MODE = 'NEW_AND_OLD_IMAGES' ) `, prefix, tableName) + return s.ExecuteSchemeQuery(ctx, query) }) if err != nil { return fmt.Errorf("failed to add changefeed to test table: %w", err) } + return nil } @@ -90,6 +93,7 @@ VALUES ) _ = c.DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { _, err := tx.Execute(ctx, query, params) + return err }) @@ -114,6 +118,7 @@ WHERE id=$id ) _ = c.DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { _, err := tx.Execute(ctx, query, params) + return err }) diff --git a/examples/topic/topicreader/topicreader_advanced.go b/examples/topic/topicreader/topicreader_advanced.go index cc1cb29be..962f4700b 100644 --- a/examples/topic/topicreader/topicreader_advanced.go +++ b/examples/topic/topicreader/topicreader_advanced.go @@ -36,6 +36,7 @@ func (m *MyMessage) UnmarshalYDBTopicMessage(data []byte) error { m.ID = data[0] m.ChangeType = data[1] m.Delta = binary.BigEndian.Uint32(data[2:]) + return nil } diff --git a/examples/topic/topicreader/topicreader_simple.go b/examples/topic/topicreader/topicreader_simple.go index a652567c5..6fb568037 100644 --- a/examples/topic/topicreader/topicreader_simple.go +++ b/examples/topic/topicreader/topicreader_simple.go @@ -5,10 +5,9 @@ import ( "fmt" "io" - firestore "google.golang.org/genproto/firestore/bundle" - "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicreader" "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicsugar" + firestore "google.golang.org/genproto/firestore/bundle" ) // PrintMessageContent is simple example for easy start read messages diff --git a/examples/topic/topicreader/topicreader_trace.go b/examples/topic/topicreader/topicreader_trace.go index 2de678993..28ab11427 100644 --- a/examples/topic/topicreader/topicreader_trace.go +++ b/examples/topic/topicreader/topicreader_trace.go @@ -44,6 +44,7 @@ func ExplicitPartitionStartStopHandler(ctx context.Context, db *ydb.Driver) { if err != nil { stopReader() } + return nil }, OnReaderPartitionReadStopResponse: func( @@ -57,6 +58,7 @@ func ExplicitPartitionStartStopHandler(ctx context.Context, db *ydb.Driver) { stopReader() } } + return nil }, }, @@ -107,6 +109,7 @@ func PartitionStartStopHandlerAndOwnReadProgressStorage(ctx context.Context, db if err != nil { stopReader() } + return nil } @@ -121,6 +124,7 @@ func PartitionStartStopHandlerAndOwnReadProgressStorage(ctx context.Context, db stopReader() } } + return nil } diff --git a/examples/topic/topicwriter/topicwriter.go b/examples/topic/topicwriter/topicwriter.go index 7faecd584..a378f8a90 100644 --- a/examples/topic/topicwriter/topicwriter.go +++ b/examples/topic/topicwriter/topicwriter.go @@ -12,16 +12,19 @@ import ( func ConnectSimple(ctx context.Context, db *ydb.Driver) *topicwriter.Writer { writer, _ := db.Topic().StartWriter("topicName") + return writer } func ConnectWithSyncWrite(ctx context.Context, db *ydb.Driver) *topicwriter.Writer { writer, _ := db.Topic().StartWriter("topicName", topicoptions.WithSyncWrite(true)) + return writer } func ConnectSelectCodec(ctx context.Context, db *ydb.Driver) *topicwriter.Writer { writer, _ := db.Topic().StartWriter("topicName", topicoptions.WithCodec(topictypes.CodecGzip)) + return writer } diff --git a/examples/ttl/main.go b/examples/ttl/main.go index ff7836037..b1a8ab963 100644 --- a/examples/ttl/main.go +++ b/examples/ttl/main.go @@ -8,7 +8,6 @@ import ( "path" 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" ) diff --git a/examples/ttl/series.go b/examples/ttl/series.go index 9aff311ea..a6e4753c3 100644 --- a/examples/ttl/series.go +++ b/examples/ttl/series.go @@ -62,6 +62,7 @@ func readExpiredBatchTransaction(ctx context.Context, c table.Client, prefix str table.ValueParam("$prev_timestamp", types.Uint64Value(prevTimestamp)), table.ValueParam("$prev_doc_id", types.Uint64Value(prevDocID)), )) + return err }, ) @@ -71,6 +72,7 @@ func readExpiredBatchTransaction(ctx context.Context, c table.Client, prefix str if res.Err() != nil { return nil, res.Err() } + return res, nil } @@ -97,9 +99,11 @@ func deleteDocumentWithTimestamp(ctx context.Context, table.ValueParam("$doc_id", types.Uint64Value(lastDocID)), table.ValueParam("$timestamp", types.Uint64Value(timestamp)), )) + return err }, ) + return err } @@ -137,12 +141,14 @@ func deleteExpired(ctx context.Context, c table.Client, prefix string, queue, ti return err } } + return res.Err() }() if err != nil { return err } } + return nil } @@ -196,9 +202,11 @@ func readDocument(ctx context.Context, c table.Client, prefix, url string) error } else { fmt.Println("\tNot found") } + return res.Err() }, ) + return err } @@ -234,9 +242,11 @@ func addDocument(ctx context.Context, c table.Client, prefix, url, html string, table.ValueParam("$html", types.TextValue(html)), table.ValueParam("$timestamp", types.Uint64Value(timestamp)), )) + return err }, ) + return err } diff --git a/examples/ttl_readtable/main.go b/examples/ttl_readtable/main.go index 54fd72d27..e867056d7 100644 --- a/examples/ttl_readtable/main.go +++ b/examples/ttl_readtable/main.go @@ -8,7 +8,6 @@ import ( "path" 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" ) diff --git a/examples/ttl_readtable/series.go b/examples/ttl_readtable/series.go index cb6fb62ec..0ed5c33a3 100644 --- a/examples/ttl_readtable/series.go +++ b/examples/ttl_readtable/series.go @@ -47,6 +47,7 @@ func deleteExpiredDocuments(ctx context.Context, c table.Client, prefix string, for i := range ids { k[i] = types.StructValue(types.StructFieldValue("doc_id", types.Uint64Value(ids[i]))) } + return k }()...) @@ -60,9 +61,11 @@ func deleteExpiredDocuments(ctx context.Context, c table.Client, prefix string, table.ValueParam("$timestamp", types.Uint64Value(timestamp)), ), ) + return err }, ) + return err } @@ -78,6 +81,7 @@ func deleteExpiredRange(ctx context.Context, c table.Client, prefix string, time options.ReadKeyRange(keyRange), options.ReadColumn("doc_id"), options.ReadColumn("ts")) + return err }, ) @@ -136,6 +140,7 @@ func deleteExpired(ctx context.Context, c table.Client, prefix string, timestamp err := c.Do(ctx, func(ctx context.Context, s table.Session) (err error) { res, err = s.DescribeTable(ctx, path.Join(prefix, "documents"), options.WithShardKeyBounds()) + return err }, ) @@ -177,6 +182,7 @@ func readDocument(ctx context.Context, c table.Client, prefix, url string) error _, res, err = s.Execute(ctx, readTx, query, table.NewQueryParameters( table.ValueParam("$url", types.TextValue(url))), ) + return err }, ) @@ -239,9 +245,11 @@ func addDocument(ctx context.Context, c table.Client, prefix, url, html string, table.ValueParam("$html", types.TextValue(html)), table.ValueParam("$timestamp", types.Uint64Value(timestamp))), ) + return err }, ) + return err } diff --git a/go.mod b/go.mod index db13a5944..339491ffc 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,23 @@ module github.com/ydb-platform/ydb-go-sdk/v3 -go 1.20 +go 1.21 require ( github.com/golang-jwt/jwt/v4 v4.4.1 github.com/google/uuid v1.3.0 github.com/jonboulle/clockwork v0.3.0 - github.com/ydb-platform/ydb-go-genproto v0.0.0-20231215113745-46f6d30f974a + github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf 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 ( - go.uber.org/mock v0.3.1-0.20231011042131-892b665398ec // indirect - github.com/rekby/fixenv v0.3.2 + github.com/rekby/fixenv v0.6.1 github.com/stretchr/testify v1.7.1 + go.uber.org/mock v0.4.0 ) require ( diff --git a/go.sum b/go.sum index f58f0f4f3..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,38 +57,33 @@ 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= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +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= 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= @@ -98,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= @@ -106,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= @@ -122,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= @@ -159,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 2c8c7b176..378e8d244 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) } @@ -110,6 +129,7 @@ type boolAllocator struct { func (a *boolAllocator) Bool() (v *Ydb.Value_BoolValue) { v = boolPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -128,6 +148,7 @@ type bytesAllocator struct { func (a *bytesAllocator) Bytes() (v *Ydb.Value_BytesValue) { v = bytesPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -146,6 +167,7 @@ type decimalAllocator struct { func (a *decimalAllocator) Decimal() (v *Ydb.DecimalType) { v = decimalPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -164,6 +186,7 @@ type dictAllocator struct { func (a *dictAllocator) Dict() (v *Ydb.DictType) { v = dictPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -182,6 +205,7 @@ type doubleAllocator struct { func (a *doubleAllocator) Double() (v *Ydb.Value_DoubleValue) { v = doublePool.Get() a.allocations = append(a.allocations, v) + return v } @@ -200,6 +224,7 @@ type floatAllocator struct { func (a *floatAllocator) Float() (v *Ydb.Value_FloatValue) { v = floatPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -218,6 +243,7 @@ type int32Allocator struct { func (a *int32Allocator) Int32() (v *Ydb.Value_Int32Value) { v = int32Pool.Get() a.allocations = append(a.allocations, v) + return v } @@ -236,6 +262,7 @@ type int64Allocator struct { func (a *int64Allocator) Int64() (v *Ydb.Value_Int64Value) { v = int64Pool.Get() a.allocations = append(a.allocations, v) + return v } @@ -254,6 +281,7 @@ type listAllocator struct { func (a *listAllocator) List() (v *Ydb.ListType) { v = listPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -272,6 +300,7 @@ type low128Allocator struct { func (a *low128Allocator) Low128() (v *Ydb.Value_Low_128) { v = low128Pool.Get() a.allocations = append(a.allocations, v) + return v } @@ -290,6 +319,7 @@ type nestedAllocator struct { func (a *nestedAllocator) Nested() (v *Ydb.Value_NestedValue) { v = nestedPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -308,6 +338,7 @@ type nullFlagAllocator struct { func (a *nullFlagAllocator) NullFlag() (v *Ydb.Value_NullFlagValue) { v = nullFlagPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -326,6 +357,7 @@ type optionalAllocator struct { func (a *optionalAllocator) Optional() (v *Ydb.OptionalType) { v = optionalPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -344,6 +376,7 @@ type pairAllocator struct { func (a *pairAllocator) Pair() (v *Ydb.ValuePair) { v = pairPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -361,16 +394,17 @@ type structAllocator struct { func (a *structAllocator) Struct() (v *Ydb.StructType) { v = structPool.Get() - if cap(v.Members) <= 0 { + if cap(v.GetMembers()) <= 0 { v.Members = make([]*Ydb.StructMember, 0, 10) } a.allocations = append(a.allocations, v) + return v } func (a *structAllocator) free() { for _, v := range a.allocations { - members := v.Members + members := v.GetMembers() for i := range members { members[i] = nil } @@ -388,6 +422,7 @@ type structMemberAllocator struct { func (a *structMemberAllocator) StructMember() (v *Ydb.StructMember) { v = structMemberPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -406,6 +441,7 @@ type textAllocator struct { func (a *textAllocator) Text() (v *Ydb.Value_TextValue) { v = textPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -424,12 +460,13 @@ type tupleAllocator struct { func (a *tupleAllocator) Tuple() (v *Ydb.TupleType) { v = tuplePool.Get() a.allocations = append(a.allocations, v) + return v } func (a *tupleAllocator) free() { for _, v := range a.allocations { - elements := v.Elements + elements := v.GetElements() for i := range elements { elements[i] = nil } @@ -447,6 +484,7 @@ type typeDecimalAllocator struct { func (a *typeDecimalAllocator) TypeDecimal() (v *Ydb.Type_DecimalType) { v = typeDecimalPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -465,6 +503,7 @@ type typeDictAllocator struct { func (a *typeDictAllocator) TypeDict() (v *Ydb.Type_DictType) { v = typeDictPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -483,6 +522,7 @@ type typeEmptyListAllocator struct { func (a *typeEmptyListAllocator) TypeEmptyList() (v *Ydb.Type_EmptyListType) { v = typeEmptyListPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -501,6 +541,7 @@ type typeEmptyDictAllocator struct { func (a *typeEmptyDictAllocator) TypeEmptyDict() (v *Ydb.Type_EmptyDictType) { v = typeEmptyDictPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -519,6 +560,7 @@ type typeAllocator struct { func (a *typeAllocator) Type() (v *Ydb.Type) { v = typePool.Get() a.allocations = append(a.allocations, v) + return v } @@ -537,6 +579,7 @@ type typeListAllocator struct { func (a *typeListAllocator) TypeList() (v *Ydb.Type_ListType) { v = typeListPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -555,6 +598,7 @@ type typeOptionalAllocator struct { func (a *typeOptionalAllocator) TypeOptional() (v *Ydb.Type_OptionalType) { v = typeOptionalPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -573,6 +617,7 @@ type typeStructAllocator struct { func (a *typeStructAllocator) TypeStruct() (v *Ydb.Type_StructType) { v = typeStructPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -591,6 +636,7 @@ type typeTupleAllocator struct { func (a *typeTupleAllocator) TypeTuple() (v *Ydb.Type_TupleType) { v = typeTuplePool.Get() a.allocations = append(a.allocations, v) + return v } @@ -609,6 +655,7 @@ type typeVariantAllocator struct { func (a *typeVariantAllocator) TypeVariant() (v *Ydb.Type_VariantType) { v = typeVariantPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -627,6 +674,7 @@ type typedValueAllocator struct { func (a *typedValueAllocator) TypedValue() (v *Ydb.TypedValue) { v = typedValuePool.Get() a.allocations = append(a.allocations, v) + return v } @@ -645,6 +693,7 @@ type uint32Allocator struct { func (a *uint32Allocator) Uint32() (v *Ydb.Value_Uint32Value) { v = uint32Pool.Get() a.allocations = append(a.allocations, v) + return v } @@ -663,6 +712,7 @@ type uint64Allocator struct { func (a *uint64Allocator) Uint64() (v *Ydb.Value_Uint64Value) { v = uint64Pool.Get() a.allocations = append(a.allocations, v) + return v } @@ -681,13 +731,14 @@ type valueAllocator struct { func (a *valueAllocator) Value() (v *Ydb.Value) { v = valuePool.Get() a.allocations = append(a.allocations, v) + return v } 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 } @@ -709,6 +760,7 @@ type variantAllocator struct { func (a *variantAllocator) Variant() (v *Ydb.VariantType) { v = variantPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -726,6 +778,7 @@ type variantStructItemsAllocator struct { func (a *variantStructItemsAllocator) VariantStructItems() (v *Ydb.VariantType_StructItems) { v = variantStructItemsPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -743,6 +796,7 @@ type variantTupleItemsAllocator struct { func (a *variantTupleItemsAllocator) VariantTupleItems() (v *Ydb.VariantType_TupleItems) { v = variantTupleItemsPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -760,6 +814,7 @@ type tableExecuteQueryResultAllocator struct { func (a *tableExecuteQueryResultAllocator) TableExecuteQueryResult() (v *Ydb_Table.ExecuteQueryResult) { v = tableExecuteQueryResultPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -778,6 +833,7 @@ type tableExecuteQueryRequestAllocator struct { func (a *tableExecuteQueryRequestAllocator) TableExecuteDataQueryRequest() (v *Ydb_Table.ExecuteDataQueryRequest) { v = tableExecuteDataQueryRequestPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -796,6 +852,7 @@ type tableQueryCachePolicyAllocator struct { func (a *tableQueryCachePolicyAllocator) TableQueryCachePolicy() (v *Ydb_Table.QueryCachePolicy) { v = tableQueryCachePolicyPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -814,6 +871,7 @@ type tableQueryAllocator struct { func (a *tableQueryAllocator) TableQuery() (v *Ydb_Table.Query) { v = tableQueryPool.Get() a.allocations = append(a.allocations, v) + return v } @@ -833,6 +891,7 @@ func (a *tableQueryYqlTextAllocator) TableQueryYqlText(s string) (v *Ydb_Table.Q v = tableQueryYqlTextPool.Get() v.YqlText = s a.allocations = append(a.allocations, v) + return v } @@ -851,6 +910,7 @@ func (a *tableQueryIDAllocator) TableQueryID(id string) (v *Ydb_Table.Query_Id) v = tableQueryIDPool.Get() v.Id = id a.allocations = append(a.allocations, v) + return v } @@ -861,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 { @@ -869,6 +1106,7 @@ func (p *Pool[T]) Get() *T { var zero T v = &zero } + return v.(*T) } @@ -877,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 3b989d66d..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) @@ -77,6 +73,7 @@ func (b *Worker) Close(ctx context.Context, err error) error { b.m.WithLock(func() { if b.closed { resErr = xerrors.WithStackTrace(ErrAlreadyClosed) + return } diff --git a/internal/background/worker_test.go b/internal/background/worker_test.go index 6a3c13748..2a188fb00 100644 --- a/internal/background/worker_test.go +++ b/internal/background/worker_test.go @@ -4,13 +4,13 @@ import ( "context" "runtime" "sync" + "sync/atomic" "testing" "time" "github.com/stretchr/testify/require" "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" ) @@ -75,7 +75,7 @@ func TestWorkerClose(t *testing.T) { w := NewWorker(ctx) started := make(empty.Chan) - stopped := xatomic.Bool{} + stopped := atomic.Bool{} w.Start("test", func(innerCtx context.Context) { close(started) <-innerCtx.Done() @@ -101,12 +101,12 @@ func TestWorkerConcurrentStartAndClose(t *testing.T) { parallel := runtime.GOMAXPROCS(0) - var counter xatomic.Int64 + var counter atomic.Int64 ctx := xtest.Context(t) w := NewWorker(ctx) - stopNewStarts := xatomic.Bool{} + stopNewStarts := atomic.Bool{} var wgStarters sync.WaitGroup for i := 0; i < parallel; i++ { wgStarters.Add(1) diff --git a/internal/backoff/backoff.go b/internal/backoff/backoff.go index c0c1fcb64..c3a6902fe 100644 --- a/internal/backoff/backoff.go +++ b/internal/backoff/backoff.go @@ -86,11 +86,12 @@ 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) } } + return b } @@ -106,6 +107,7 @@ func (b logBackoff) Delay(i int) time.Duration { if f == d { return f } + return f + time.Duration(b.r.Int64(int64(d-f)+1)) } @@ -113,6 +115,7 @@ func min(a, b uint) uint { if a < b { return a } + return b } @@ -120,5 +123,6 @@ func max(a, b uint) uint { if a > b { return a } + return b } diff --git a/internal/backoff/backoff_test.go b/internal/backoff/backoff_test.go index 24cb77f8c..45cd7fd1e 100644 --- a/internal/backoff/backoff_test.go +++ b/internal/backoff/backoff_test.go @@ -15,6 +15,7 @@ func TestDelays(t *testing.T) { if err != nil { panic(err) } + return d } b := New( @@ -141,6 +142,7 @@ func TestLogBackoff(t *testing.T) { act, eq, ) } + continue } if gte := exp.gte; act <= gte { @@ -172,6 +174,7 @@ func TestFastSlowDelaysWithoutJitter(t *testing.T) { backoff: func() (backoff logBackoff) { backoff = Fast backoff.jitterLimit = 1 + return backoff }(), exp: []time.Duration{ @@ -193,6 +196,7 @@ func TestFastSlowDelaysWithoutJitter(t *testing.T) { backoff: func() (backoff logBackoff) { backoff = Slow backoff.jitterLimit = 1 + return backoff }(), exp: []time.Duration{ diff --git a/internal/balancer/balancer.go b/internal/balancer/balancer.go index 5f089688f..f69ec11e2 100644 --- a/internal/balancer/balancer.go +++ b/internal/balancer/balancer.go @@ -55,6 +55,7 @@ func (b *Balancer) HasNode(id uint32) bool { if _, has := b.connectionsState.connByNodeID[id]; has { return true } + return false } @@ -80,8 +81,10 @@ func (b *Balancer) clusterDiscovery(ctx context.Context) (err error) { if ctx.Err() == nil && xerrors.IsTimeoutError(err) { return xerrors.WithStackTrace(xerrors.Retryable(err)) } + return xerrors.WithStackTrace(err) } + return nil }, retry.WithIdempotent(true), @@ -94,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 @@ -162,6 +166,7 @@ func endpointsDiff(newestEndpoints []endpoint.Endpoint, previousConns []conn.Con dropped = append(dropped, c.Endpoint().Copy()) } } + return nodes, added, dropped } @@ -169,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) @@ -207,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) @@ -233,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, @@ -253,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 @@ -279,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()), @@ -311,11 +314,13 @@ func (b *Balancer) NewStream( var client grpc.ClientStream err = b.wrapCall(ctx, func(ctx context.Context, cc conn.Conn) error { client, err = cc.NewStream(ctx, desc, method, opts...) + return err }) if err == nil { return client, nil } + return nil, err } @@ -348,8 +353,10 @@ func (b *Balancer) wrapCall(ctx context.Context, f func(ctx context.Context, cc credentials.WithCredentials(b.driverConfig.Credentials()), ) } + return xerrors.WithStackTrace(err) } + return err } @@ -366,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 { @@ -397,6 +404,7 @@ func (b *Balancer) getConn(ctx context.Context) (c conn.Conn, err error) { fmt.Errorf("%w: cannot get connection from Balancer after %d attempts", ErrNoEndpoints, failedCount), ) } + return c, nil } @@ -405,5 +413,6 @@ func endpointsToConnections(p *conn.Pool, endpoints []endpoint.Endpoint) []conn. for _, e := range endpoints { conns = append(conns, p.Get(e)) } + return conns } diff --git a/internal/balancer/connections_state.go b/internal/balancer/connections_state.go index 98afeba68..e9196ead7 100644 --- a/internal/balancer/connections_state.go +++ b/internal/balancer/connections_state.go @@ -35,6 +35,7 @@ func newConnectionsState( } else { res.all = res.prefer } + return res } @@ -54,6 +55,7 @@ func (s *connectionsState) GetConnection(ctx context.Context) (_ conn.Conn, fail try := func(conns []conn.Conn) conn.Conn { c, tryFailed := s.selectRandomConnection(conns, false) failedCount += tryFailed + return c } @@ -121,6 +123,7 @@ func connsToNodeIDMap(conns []conn.Conn) (nodes map[uint32]conn.Conn) { for _, c := range conns { nodes[c.Endpoint().NodeID()] = c } + return nodes } @@ -146,6 +149,7 @@ func sortPreferConnections( fallback = append(fallback, c) } } + return prefer, fallback } diff --git a/internal/balancer/ctx.go b/internal/balancer/ctx.go index 434e64ede..9b4aeb209 100644 --- a/internal/balancer/ctx.go +++ b/internal/balancer/ctx.go @@ -18,5 +18,6 @@ func ContextEndpoint(ctx context.Context) (e Endpoint, ok bool) { if e, ok = ctx.Value(ctxEndpointKey{}).(Endpoint); ok { return e, true } + return nil, false } diff --git a/internal/balancer/local_dc.go b/internal/balancer/local_dc.go index d375ac680..e764af7d8 100644 --- a/internal/balancer/local_dc.go +++ b/internal/balancer/local_dc.go @@ -75,16 +75,19 @@ func detectFastestEndpoint(ctx context.Context, endpoints []endpoint.Endpoint) ( host, port, err := extractHostPort(ep.Address()) if err != nil { lastErr = xerrors.WithStackTrace(err) + continue } addresses, err := net.DefaultResolver.LookupHost(ctx, host) if err != nil { lastErr = err + continue } if len(addresses) == 0 { lastErr = xerrors.WithStackTrace(fmt.Errorf("no ips for fqdn: %q", host)) + continue } @@ -105,6 +108,7 @@ func detectFastestEndpoint(ctx context.Context, endpoints []endpoint.Endpoint) ( if fastestAddress == "" { return nil, xerrors.WithStackTrace(errors.New("failed to check fastest address")) } + return addressToEndpoint[fastestAddress], nil } @@ -127,6 +131,7 @@ func detectLocalDC(ctx context.Context, endpoints []endpoint.Endpoint) (string, if err == nil { return fastest.Location(), nil } + return "", err } @@ -143,6 +148,7 @@ func extractHostPort(address string) (host, port string, _ error) { if err != nil { return "", "", xerrors.WithStackTrace(err) } + return host, port, nil } @@ -174,5 +180,6 @@ func splitEndpointsByLocation(endpoints []endpoint.Endpoint) map[string][]endpoi location := ep.Location() res[location] = append(res[location], ep) } + return res } diff --git a/internal/bind/bind.go b/internal/bind/bind.go index a19699a88..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,38 +27,40 @@ 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 { sort.Slice(bindings, func(i, j int) bool { return bindings[i].blockID() < bindings[j].blockID() }) + return bindings } diff --git a/internal/bind/numeric_args.go b/internal/bind/numeric_args.go index ecfc22787..89c9c4ecc 100644 --- a/internal/bind/numeric_args.go +++ b/internal/bind/numeric_args.go @@ -76,6 +76,7 @@ func (m NumericArgs) RewriteQuery(sql string, args ...interface{}) ( if len(newArgs) > 0 { const prefix = "-- origin query with numeric args replacement\n" + return prefix + buffer.String(), newArgs, nil } @@ -101,18 +102,21 @@ func numericArgsStateFn(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos-width]) } l.start = l.pos + return numericArgState } case '-': nextRune, width := utf8.DecodeRuneInString(l.src[l.pos:]) if nextRune == '-' { l.pos += width + return oneLineCommentState } case '/': nextRune, width := utf8.DecodeRuneInString(l.src[l.pos:]) if nextRune == '*' { l.pos += width + return multilineCommentState } case utf8.RuneError: @@ -120,6 +124,7 @@ func numericArgsStateFn(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } @@ -149,9 +154,11 @@ func numericArgState(l *sqlLexer) stateFn { numbers += string(r) case isLetter(r): numbers = "" + return l.rawStateFn default: l.pos -= width + return l.rawStateFn } } diff --git a/internal/bind/params.go b/internal/bind/params.go index 00df0504d..d0e6a82da 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" ) @@ -46,6 +46,7 @@ func toValue(v interface{}) (_ types.Value, err error) { return types.NullValue(types.TypeInt32), nil } xx := int32(*x) + return types.NullableInt32Value(&xx), nil case uint: return types.Uint32Value(uint32(x)), nil @@ -54,6 +55,7 @@ func toValue(v interface{}) (_ types.Value, err error) { return types.NullValue(types.TypeUint32), nil } xx := uint32(*x) + return types.NullableUint32Value(&xx), nil case int8: return types.Int8Value(x), nil @@ -108,6 +110,7 @@ func toValue(v interface{}) (_ types.Value, err error) { for i := range x { items[i] = types.TextValue(x[i]) } + return types.ListValue(items...), nil case [16]byte: return types.UUIDValue(x), nil @@ -135,10 +138,11 @@ func supportNewTypeLink(x interface{}) string { v.Add("labels", "enhancement,database/sql") v.Add("template", "02_FEATURE_REQUEST.md") v.Add("title", fmt.Sprintf("feat: Support new type `%T` in `database/sql` query args", x)) + 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 != "" { @@ -153,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) @@ -166,39 +170,38 @@ func toYdbParam(name string, value interface{}) (table.ParameterOption, error) { if name[0] != '$' { 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{}) (parameters []*params.Parameter, _ error) { + parameters = make([]*params.Parameter, 0, len(args)) for i, arg := range args { switch x := arg.(type) { case driver.NamedValue: if x.Name == "" { switch xx := x.Value.(type) { - case *table.QueryParameters: + case *params.Parameters: 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) + parameters = *xx + case *params.Parameter: + parameters = append(parameters, 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) + parameters = append(parameters, param) } } else { param, err := toYdbParam(x.Name, x.Value) if err != nil { return nil, xerrors.WithStackTrace(err) } - params = append(params, param) + parameters = append(parameters, param) } case sql.NamedArg: if x.Name == "" { @@ -208,26 +211,25 @@ func Params(args ...interface{}) (params []table.ParameterOption, _ error) { if err != nil { return nil, xerrors.WithStackTrace(err) } - params = append(params, param) - case *table.QueryParameters: + parameters = append(parameters, param) + 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: + parameters = append(parameters, x) default: param, err := toYdbParam(fmt.Sprintf("$p%d", i), x) if err != nil { return nil, xerrors.WithStackTrace(err) } - params = append(params, param) + parameters = append(parameters, param) } } - sort.Slice(params, func(i, j int) bool { - return params[i].Name() < params[j].Name() + sort.Slice(parameters, func(i, j int) bool { + return parameters[i].Name() < parameters[j].Name() }) - return params, nil + + return parameters, 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/bind/positional_args.go b/internal/bind/positional_args.go index 506a3d62e..e3c0afa13 100644 --- a/internal/bind/positional_args.go +++ b/internal/bind/positional_args.go @@ -65,6 +65,7 @@ func (m PositionalArgs) RewriteQuery(sql string, args ...interface{}) ( if position > 0 { const prefix = "-- origin query with positional args replacement\n" + return prefix + buffer.String(), newArgs, nil } @@ -90,12 +91,14 @@ func positionalArgsStateFn(l *sqlLexer) stateFn { nextRune, width := utf8.DecodeRuneInString(l.src[l.pos:]) if nextRune == '-' { l.pos += width + return oneLineCommentState } case '/': nextRune, width := utf8.DecodeRuneInString(l.src[l.pos:]) if nextRune == '*' { l.pos += width + return multilineCommentState } case utf8.RuneError: @@ -103,6 +106,7 @@ func positionalArgsStateFn(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } diff --git a/internal/bind/sql_lexer.go b/internal/bind/sql_lexer.go index 23626b495..49fe30911 100644 --- a/internal/bind/sql_lexer.go +++ b/internal/bind/sql_lexer.go @@ -44,6 +44,7 @@ func backtickState(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } @@ -66,6 +67,7 @@ func singleQuoteState(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } @@ -88,6 +90,7 @@ func doubleQuoteState(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } @@ -109,6 +112,7 @@ func oneLineCommentState(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } @@ -143,6 +147,7 @@ func multilineCommentState(l *sqlLexer) stateFn { l.parts = append(l.parts, l.src[l.start:l.pos]) l.start = l.pos } + return nil } } diff --git a/internal/certificates/certificates.go b/internal/certificates/certificates.go index 6a7b1f12f..8282dce79 100644 --- a/internal/certificates/certificates.go +++ b/internal/certificates/certificates.go @@ -47,6 +47,7 @@ func loadFromFileCache(key string) (_ []*x509.Certificate, exists bool) { if !ok { panic(fmt.Sprintf("unexpected value type '%T'", value)) } + return certs, true } @@ -65,6 +66,7 @@ func FromFile(file string, opts ...FromFileOption) ([]*x509.Certificate, error) if options.onHit != nil { options.onHit() } + return certs, nil } } @@ -100,6 +102,7 @@ func loadFromPemCache(key string) (_ *x509.Certificate, exists bool) { if !ok { panic(fmt.Sprintf("unexpected value type '%T'", value)) } + return cert, true } @@ -120,6 +123,7 @@ func parseCertificate(der []byte, opts ...FromPemOption) (*x509.Certificate, err if options.onHit != nil { options.onHit() } + return cert, nil } } diff --git a/internal/cmd/gstack/main.go b/internal/cmd/gstack/main.go new file mode 100644 index 000000000..12f427668 --- /dev/null +++ b/internal/cmd/gstack/main.go @@ -0,0 +1,222 @@ +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.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 95a72f0c2..99f2bf98a 100644 --- a/internal/cmd/gtrace/main.go +++ b/internal/cmd/gtrace/main.go @@ -16,15 +16,10 @@ import ( "os" "path/filepath" "strings" - _ "unsafe" // For go:linkname. "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" ) -//go:linkname build_goodOSArchFile go/build.(*Context).goodOSArchFile -//nolint:revive -func build_goodOSArchFile(*build.Context, string, map[string]bool) bool - //nolint:gocyclo func main() { var ( @@ -64,8 +59,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 @@ -79,9 +72,12 @@ func main() { if err != nil { log.Fatal(err) } + 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, @@ -102,7 +98,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 } @@ -159,6 +155,7 @@ func main() { if n == nil { item = nil depth-- + return true } defer func() { @@ -175,6 +172,7 @@ func main() { if item != nil { item.Ident = v } + return false case *ast.CommentGroup: @@ -185,6 +183,7 @@ func main() { } } } + return false case *ast.StructType: @@ -193,6 +192,7 @@ func main() { items = append(items, item) item = nil } + return false } @@ -228,6 +228,7 @@ func main() { "skipping hook %s due to error: %v", name, err, ) + continue } t.Hooks = append(t.Hooks, Hook{ @@ -289,12 +290,14 @@ func buildFunc(info *types.Info, traces map[string]*Trace, fn *ast.FuncType) (re return nil, xerrors.WithStackTrace(err) } ret.Result = append(ret.Result, result) + return ret, nil case *ast.Ident: if t, ok := traces[x.Name]; ok { t.Nested = true ret.Result = append(ret.Result, t) + return ret, nil } } @@ -305,37 +308,6 @@ func buildFunc(info *types.Info, traces map[string]*Trace, fn *ast.FuncType) (re ) } -func splitOSArchTags(ctx *build.Context, name string) (base, tags, ext string) { - fileTags := make(map[string]bool) - build_goodOSArchFile(ctx, name, fileTags) - ext = filepath.Ext(name) - switch len(fileTags) { - case 0: // * - base = strings.TrimSuffix(name, ext) - - case 1: // *_GOOS or *_GOARCH - i := strings.LastIndexByte(name, '_') - - base = name[:i] - tags = strings.TrimSuffix(name[i:], ext) - - case 2: // *_GOOS_GOARCH - var i int - i = strings.LastIndexByte(name, '_') - i = strings.LastIndexByte(name[:i], '_') - - base = name[:i] - tags = strings.TrimSuffix(name[i:], ext) - - default: - panic(fmt.Sprintf( - "gtrace: internal error: unexpected number of OS/arch tags: %d", - len(fileTags), - )) - } - return -} - type Package struct { *types.Package @@ -396,6 +368,7 @@ func rsplit(s string, c byte) (s1, s2 string) { if i == -1 { return s, "" } + return s[:i], s[i+1:] } @@ -411,6 +384,7 @@ func scanBuildConstraints(r io.Reader) (cs []string, err error) { comm = bytes.TrimSpace(comm) if bytes.HasPrefix(comm, []byte("+build")) { cs = append(cs, string(line)) + continue } } @@ -418,6 +392,7 @@ func scanBuildConstraints(r io.Reader) (cs []string, err error) { break } } + return cs, nil } @@ -428,5 +403,6 @@ func isGenerated(base, suffix string) bool { } n := len(base) m := i + len(suffix) + return m == n || base[m] == '_' } diff --git a/internal/cmd/gtrace/writer.go b/internal/cmd/gtrace/writer.go index 00e099ba6..fcc87a43e 100644 --- a/internal/cmd/gtrace/writer.go +++ b/internal/cmd/gtrace/writer.go @@ -160,6 +160,7 @@ func (w *Writer) typeImports(dst []dep, t types.Type) []dep { typName: obj.Name(), }) } + return dst } @@ -175,6 +176,7 @@ func unwrapStruct(t types.Type) (n *types.Named, s *types.Struct) { if ok { s, _ = n.Underlying().(*types.Struct) } + return } @@ -194,6 +196,7 @@ func (w *Writer) funcImports(dst []dep, fn *Func) []dep { dst = w.funcImports(dst, fn) } } + return dst } @@ -201,6 +204,7 @@ func (w *Writer) traceImports(dst []dep, t *Trace) []dep { for _, h := range t.Hooks { dst = w.funcImports(dst, h.Func) } + return dst } @@ -212,6 +216,7 @@ func (w *Writer) importDeps(deps []dep) { n := len(deps) deps[i], deps[n-1] = deps[n-1], deps[i] deps = deps[:n-1] + continue } seen[d.pkgPath] = true @@ -230,6 +235,7 @@ func (w *Writer) importDeps(deps []dep) { if std0 != std1 { return std0 } + return d0.pkgPath < d1.pkgPath }) w.line(`import (`) @@ -250,6 +256,7 @@ func (w *Writer) importDeps(deps []dep) { func (w *Writer) isStdLib(pkg string) bool { w.ensureStdLibMapping() s := strings.Split(pkg, "/")[0] + return w.std[s] } @@ -520,6 +527,7 @@ func (w *Writer) hookFuncCall(fn *Func, name string, args []string) { }) w.line(`}`) }) + return } } @@ -532,6 +540,7 @@ func nameParam(p *Param) (s string) { if s == "" { s = firstChar(ident(typeBasename(p.Type))) } + return unexported(s) } @@ -540,6 +549,7 @@ func (w *Writer) declareParams(src []Param) (names []string) { for i := range src { names[i] = w.declare(nameParam(&src[i])) } + return names } @@ -548,10 +558,12 @@ func flattenParams(params []Param) (dst []Param) { _, s := unwrapStruct(params[i].Type) if s != nil { dst = flattenStruct(dst, s) + continue } dst = append(dst, params[i]) } + return dst } @@ -560,6 +572,7 @@ func typeBasename(t types.Type) (name string) { if name == "" { name = lo } + return name } @@ -582,6 +595,7 @@ func flattenStruct(dst []Param, s *types.Struct) []Param { Type: typ, }) }) + return dst } @@ -592,16 +606,18 @@ func (w *Writer) constructParams(params []Param, names []string) (res []string) var v string v, names = w.constructStruct(n, s, names) res = append(res, v) + continue } name := names[0] names = names[1:] res = append(res, name) } + return res } -func (w *Writer) constructStruct(n *types.Named, s *types.Struct, vars []string) (string, []string) { +func (w *Writer) constructStruct(n types.Type, s *types.Struct, vars []string) (string, []string) { p := w.declare("p") // maybe skip pointers from flattening to not allocate anyhing during trace. w.line(`var `, p, ` `, w.typeString(n)) @@ -614,6 +630,7 @@ func (w *Writer) constructStruct(n *types.Named, s *types.Struct, vars []string) vars = vars[1:] w.line(p, `.`, v.Name(), ` = `, name) } + return p, vars } @@ -726,6 +743,7 @@ func (w *Writer) hookFuncShortcut(fn *Func, name string) { func (w *Writer) zeroReturn(fn *Func) { if !fn.HasResult() { w.line(`return`) + return } w.code(`return `) @@ -753,6 +771,7 @@ func (w *Writer) funcParams(params []Param) (vars []string) { vars = append(vars, w.funcParam(¶ms[i])) } w.code(`)`) + return } @@ -760,6 +779,7 @@ func (w *Writer) funcParam(p *Param) (name string) { name = w.declare(nameParam(p)) w.code(name, ` `) w.code(w.typeString(p.Type)) + return name } @@ -875,6 +895,7 @@ func haveNames(params []Param) bool { return true } } + return false } @@ -883,6 +904,7 @@ func (w *Writer) typeString(t types.Type) string { if pkg.Path() == w.pkg.Path() { return "" // same package; unqualified } + return pkg.Name() }) } @@ -922,6 +944,7 @@ func exported(s string) string { if r == utf8.RuneError { panic("invalid string") } + return string(unicode.ToUpper(r)) + s[size:] } @@ -930,6 +953,7 @@ func unexported(s string) string { if r == utf8.RuneError { panic("invalid string") } + return string(unicode.ToLower(r)) + s[size:] } @@ -938,6 +962,7 @@ func firstChar(s string) string { if r == utf8.RuneError { panic("invalid string") } + return string(r) } @@ -984,6 +1009,7 @@ func tempName(names ...string) string { } sb.WriteString(name) } + return sb.String() } @@ -1006,10 +1032,12 @@ func (s *scope) set(v string) bool { s.vars[v] = decl{ where: fmt.Sprintf("%s:%d", file, line), } + return true } func (s *scope) where(v string) string { d := s.vars[v] + return d.where } 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 c9a1d755a..b9bb75105 100644 --- a/internal/conn/conn.go +++ b/internal/conn/conn.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sync" + "sync/atomic" "time" "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" @@ -16,7 +17,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/meta" "github.com/ydb-platform/ydb-go-sdk/v3/internal/response" "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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" @@ -54,8 +54,8 @@ type conn struct { done chan struct{} endpoint endpoint.Endpoint // ro access closed bool - state xatomic.Uint32 - lastUsage time.Time + state atomic.Uint32 + lastUsage *lastUsage onClose []func(*conn) onTransportErrors []func(ctx context.Context, cc Conn, cause error) } @@ -72,13 +72,15 @@ func (c *conn) Ping(ctx context.Context) error { if !isAvailable(cc) { return c.wrapError(errUnavailableConnection) } + return nil } 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 { @@ -92,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() { @@ -122,17 +132,11 @@ 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 } + return nil } @@ -144,10 +148,11 @@ 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) } + return s } @@ -163,6 +168,7 @@ func (c *conn) Unban(ctx context.Context) State { } c.setState(ctx, newState) + return newState } @@ -190,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() { @@ -207,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) }() @@ -234,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 } @@ -252,12 +256,14 @@ func (c *conn) close(ctx context.Context) (err error) { err = c.cc.Close() c.cc = nil c.setState(ctx, Offline) + return c.wrapError(err) } func (c *conn) isClosed() bool { c.mtx.RLock() defer c.mtx.RUnlock() + return c.closed } @@ -271,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() { @@ -304,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 @@ -320,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 { @@ -332,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) }() @@ -344,6 +354,7 @@ func (c *conn) Invoke( if sentMark.canRetry() { return c.wrapError(xerrors.Retryable(err, xerrors.WithName("Invoke"))) } + return c.wrapError(err) } @@ -382,9 +393,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) @@ -393,18 +404,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) @@ -412,8 +412,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 { @@ -424,6 +424,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) }() @@ -436,6 +440,7 @@ func (c *conn) NewStream( if sentMark.canRetry() { return s, c.wrapError(xerrors.Retryable(err, xerrors.WithName("NewStream"))) } + return s, c.wrapError(err) } @@ -444,15 +449,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 } @@ -461,6 +465,7 @@ func (c *conn) wrapError(err error) error { return nil } nodeErr := newConnError(c.endpoint.NodeID(), c.endpoint.Address(), err) + return xerrors.WithStackTrace(nodeErr, xerrors.WithSkipDepth(1)) } @@ -484,14 +489,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) } } @@ -530,6 +536,7 @@ var rpcKey = ctxHandleRPCKey{} func markContext(ctx context.Context) (context.Context, *modificationMark) { mark := &modificationMark{} + return context.WithValue(ctx, rpcKey, mark), mark } @@ -538,11 +545,12 @@ func getContextMark(ctx context.Context) *modificationMark { if v == nil { return &modificationMark{} } + return v.(*modificationMark) } type modificationMark struct { - dirty xatomic.Bool + dirty atomic.Bool } func (m *modificationMark) canRetry() bool { diff --git a/internal/conn/context.go b/internal/conn/context.go index 927d1c846..64ffd877e 100644 --- a/internal/conn/context.go +++ b/internal/conn/context.go @@ -10,5 +10,6 @@ func WithoutWrapping(ctx context.Context) context.Context { func UseWrapping(ctx context.Context) bool { b, ok := ctx.Value(ctxNoWrappingKey{}).(bool) + return !ok || !b } 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 a24db653e..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( @@ -38,6 +51,7 @@ func (s *grpcClientStream) CloseSend() (err error) { ), ) } + return s.wrapError(err) } @@ -45,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) }() @@ -65,6 +90,7 @@ func (s *grpcClientStream) SendMsg(m interface{}) (err error) { xerrors.WithName("SendMsg"), )) } + return s.wrapError(err) } @@ -75,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) @@ -105,6 +140,7 @@ func (s *grpcClientStream) RecvMsg(m interface{}) (err error) { xerrors.WithName("RecvMsg"), )) } + return s.wrapError(err) } @@ -132,26 +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/middleware.go b/internal/conn/middleware.go index f6069def8..07ab761e4 100644 --- a/internal/conn/middleware.go +++ b/internal/conn/middleware.go @@ -37,12 +37,14 @@ func WithContextModifier( return &middleware{ invoke: func(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error { ctx = modifyCtx(ctx) + return cc.Invoke(ctx, method, args, reply, opts...) }, newStream: func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) ( grpc.ClientStream, error, ) { ctx = modifyCtx(ctx) + return cc.NewStream(ctx, desc, method, opts...) }, } @@ -52,12 +54,14 @@ func WithAppendOptions(cc grpc.ClientConnInterface, appendOpts ...grpc.CallOptio return &middleware{ invoke: func(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error { opts = append(opts, appendOpts...) + return cc.Invoke(ctx, method, args, reply, opts...) }, newStream: func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) ( grpc.ClientStream, error, ) { opts = append(opts, appendOpts...) + return cc.NewStream(ctx, desc, method, opts...) }, } @@ -70,12 +74,14 @@ func WithBeforeFunc( return &middleware{ invoke: func(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error { before() + return cc.Invoke(ctx, method, args, reply, opts...) }, newStream: func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) ( grpc.ClientStream, error, ) { before() + return cc.NewStream(ctx, desc, method, opts...) }, } diff --git a/internal/conn/pool.go b/internal/conn/pool.go index 47e0082e8..6115c612f 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,18 +136,21 @@ 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)) } func (p *Pool) Take(context.Context) error { atomic.AddInt64(&p.usages, 1) + return nil } 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) }() @@ -201,11 +227,14 @@ func (p *Pool) collectConns() []*conn { for _, c := range p.conns { conns = append(conns, c) } + return conns } 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{ @@ -215,8 +244,10 @@ 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) + go p.connParker(xcontext.ValueOnly(ctx), ttl, ttl/2) } + return p } diff --git a/internal/coordination/client.go b/internal/coordination/client.go index 078054732..0bb225f67 100644 --- a/internal/coordination/client.go +++ b/internal/coordination/client.go @@ -33,12 +33,12 @@ type Client struct { 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 { return &Client{ config: config, service: Ydb_Coordination_V1.NewCoordinationServiceClient(cc), sessions: make(map[*session]struct{}), - }, nil + } } func (c *Client) CreateNode(ctx context.Context, path string, config coordination.NodeConfig) error { @@ -51,6 +51,7 @@ func (c *Client) CreateNode(ctx context.Context, path string, config coordinatio if !c.config.AutoRetry() { return xerrors.WithStackTrace(call(ctx)) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -79,6 +80,7 @@ func (c *Client) createNode(ctx context.Context, path string, config coordinatio ), }, ) + return xerrors.WithStackTrace(err) } @@ -92,6 +94,7 @@ func (c *Client) AlterNode(ctx context.Context, path string, config coordination if !c.config.AutoRetry() { return xerrors.WithStackTrace(call(ctx)) } + return retry.Retry(ctx, call, retry.WithStackTrace(), @@ -121,6 +124,7 @@ func (c *Client) alterNode(ctx context.Context, path string, config coordination ), }, ) + return xerrors.WithStackTrace(err) } @@ -134,6 +138,7 @@ func (c *Client) DropNode(ctx context.Context, path string) error { if !c.config.AutoRetry() { return xerrors.WithStackTrace(call(ctx)) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -154,6 +159,7 @@ func (c *Client) dropNode(ctx context.Context, path string) error { ), }, ) + return xerrors.WithStackTrace(err) } @@ -170,10 +176,12 @@ func (c *Client) DescribeNode( } call := func(ctx context.Context) (err error) { entry, config, err = c.describeNode(ctx, path) + return xerrors.WithStackTrace(err) } if !c.config.AutoRetry() { err := call(ctx) + return entry, config, xerrors.WithStackTrace(err) } err := retry.Retry(ctx, call, @@ -181,6 +189,7 @@ func (c *Client) DescribeNode( retry.WithIdempotent(true), retry.WithTrace(c.config.TraceRetry()), ) + return entry, config, xerrors.WithStackTrace(err) } @@ -216,6 +225,7 @@ func (c *Client) describeNode( if err != nil { return nil, nil, xerrors.WithStackTrace(err) } + return scheme.InnerConvertEntry(result.GetSelf()), &coordination.NodeConfig{ Path: result.GetConfig().GetPath(), SelfCheckPeriodMillis: result.GetConfig().GetSelfCheckPeriodMillis(), diff --git a/internal/coordination/config/config.go b/internal/coordination/config/config.go index c3c21dddc..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 @@ -39,10 +37,11 @@ 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) } } + return c } diff --git a/internal/credentials/access_error.go b/internal/credentials/access_error.go index ce904e2e4..777bc3d80 100644 --- a/internal/credentials/access_error.go +++ b/internal/credentials/access_error.go @@ -94,12 +94,15 @@ 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") + return xerrors.WithStackTrace(fmt.Errorf(buffer.String(), err), xerrors.WithSkipDepth(1)) } @@ -115,5 +118,6 @@ func IsAccessError(err error) bool { ) { return true } + return false } diff --git a/internal/credentials/access_token.go b/internal/credentials/access_token.go index 57e0f12bc..c6e205f32 100644 --- a/internal/credentials/access_token.go +++ b/internal/credentials/access_token.go @@ -32,8 +32,11 @@ 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 } @@ -53,5 +56,6 @@ func (c AccessToken) String() string { fmt.Fprintf(buffer, "%q", c.sourceInfo) } buffer.WriteByte('}') + return buffer.String() } diff --git a/internal/credentials/anonymous.go b/internal/credentials/anonymous.go index eea7ba2c1..88d937095 100644 --- a/internal/credentials/anonymous.go +++ b/internal/credentials/anonymous.go @@ -28,8 +28,11 @@ func NewAnonymousCredentials(opts ...AnonymousCredentialsOption) *Anonymous { sourceInfo: stack.Record(1), } for _, opt := range opts { - opt.ApplyAnonymousCredentialsOption(c) + if opt != nil { + opt.ApplyAnonymousCredentialsOption(c) + } } + return c } @@ -48,5 +51,6 @@ func (c Anonymous) String() string { fmt.Fprintf(buffer, "%q", c.sourceInfo) } buffer.WriteByte('}') + return buffer.String() } diff --git a/internal/credentials/static.go b/internal/credentials/static.go index 39bba0234..298785bf1 100644 --- a/internal/credentials/static.go +++ b/internal/credentials/static.go @@ -47,8 +47,11 @@ 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 } @@ -141,6 +144,7 @@ func parseExpiresAt(raw string) (expiresAt time.Time, err error) { if _, _, err = jwt.NewParser().ParseUnverified(raw, &claims); err != nil { return expiresAt, xerrors.WithStackTrace(err) } + return claims.ExpiresAt.Time, nil } @@ -158,5 +162,6 @@ func (c *Static) String() string { fmt.Fprintf(buffer, "%q", c.sourceInfo) } buffer.WriteByte('}') + return buffer.String() } diff --git a/internal/decimal/decimal.go b/internal/decimal/decimal.go index 4bf91f6ab..a4753992a 100644 --- a/internal/decimal/decimal.go +++ b/internal/decimal/decimal.go @@ -73,6 +73,7 @@ func FromBytes(bts []byte, precision, scale uint32) *big.Int { v.Set(inf) } } + return v } @@ -102,12 +103,14 @@ func Parse(s string, precision, scale uint32) (*big.Int, error) { if neg { return v.Set(neginf), nil } + return v.Set(inf), nil } if isNaN(s) { if neg { return v.Set(negnan), nil } + return v.Set(nan), nil } @@ -121,6 +124,7 @@ func Parse(s string, precision, scale uint32) (*big.Int, error) { return nil, syntaxError(s) } dot = true + continue } if dot { @@ -142,6 +146,7 @@ func Parse(s string, precision, scale uint32) (*big.Int, error) { if neg { return neginf, nil } + return inf, nil } integral-- @@ -176,6 +181,7 @@ func Parse(s string, precision, scale uint32) (*big.Int, error) { if neg { v.Neg(v) } + return v, nil } @@ -187,12 +193,14 @@ func Format(x *big.Int, precision, scale uint32) string { if x.Sign() < 0 { return "-inf" } + return "inf" case x.CmpAbs(nan) == 0: if x.Sign() < 0 { return "-nan" } + return "nan" case x == nil: @@ -271,6 +279,7 @@ func BigIntToByte(x *big.Int, precision, scale uint32) (p [16]byte) { } } put(x, p[:]) + return p } @@ -301,6 +310,7 @@ func Append(p []byte, x *big.Int) []byte { n := len(p) p = ensure(p, size(x)) put(x, p[n:]) + return p } @@ -308,6 +318,7 @@ func size(x *big.Int) int { if x.Sign() < 0 { x = complement(x) } + return len(x.Bits()) * wordSize } @@ -321,6 +332,7 @@ func ensure(p []byte, n int) []byte { copy(cp, p) p = cp } + return p[:l+n] } @@ -346,6 +358,7 @@ func pow(x *big.Int, n uint32) *big.Int { n >>= 1 m.Mul(m, m) } + return v } @@ -356,6 +369,7 @@ func complement(x *big.Int) *big.Int { not(x) x.Neg(x) x.Add(x, one) + return x } diff --git a/internal/decimal/decimal_test.go b/internal/decimal/decimal_test.go index 8178c37c9..fd7391da1 100644 --- a/internal/decimal/decimal_test.go +++ b/internal/decimal/decimal_test.go @@ -61,6 +61,7 @@ func uint128(hi, lo uint64) []byte { p := make([]byte, 16) binary.BigEndian.PutUint64(p[:8], hi) binary.BigEndian.PutUint64(p[8:], lo) + return p } 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 ebcdce783..782f3b6b2 100644 --- a/internal/discovery/config/config.go +++ b/internal/discovery/config/config.go @@ -29,11 +29,12 @@ 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) } } + return c } diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go index 6126c5961..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{ @@ -70,9 +70,7 @@ func (c *Client) Discover(ctx context.Context) (endpoints []endpoint.Endpoint, e if response.GetOperation().GetStatus() != Ydb.StatusIds_SUCCESS { return nil, xerrors.WithStackTrace( - xerrors.Operation( - xerrors.FromOperation(response.GetOperation()), - ), + xerrors.FromOperation(response.GetOperation()), ) } @@ -82,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()), @@ -101,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 @@ -126,10 +126,8 @@ func (c *Client) WhoAmI(ctx context.Context) (whoAmI *discovery.WhoAmI, err erro if response.GetOperation().GetStatus() != Ydb.StatusIds_SUCCESS { return nil, xerrors.WithStackTrace( - xerrors.Operation( - xerrors.FromOperation( - response.GetOperation(), - ), + xerrors.FromOperation( + response.GetOperation(), ), ) } @@ -154,5 +152,6 @@ func (c *Client) Close(context.Context) error { if cc, has := c.cc.(io.Closer); has { return cc.Close() } + return nil } diff --git a/internal/endpoint/endpoint.go b/internal/endpoint/endpoint.go index 139829cac..823d721d0 100644 --- a/internal/endpoint/endpoint.go +++ b/internal/endpoint/endpoint.go @@ -39,6 +39,7 @@ type endpoint struct { func (e *endpoint) Copy() Endpoint { e.mu.RLock() defer e.mu.RUnlock() + return &endpoint{ id: e.id, address: e.address, @@ -53,6 +54,7 @@ func (e *endpoint) Copy() Endpoint { func (e *endpoint) String() string { e.mu.RLock() defer e.mu.RUnlock() + return fmt.Sprintf(`{id:%d,address:%q,local:%t,location:%q,loadFactor:%f,lastUpdated:%q}`, e.id, e.address, @@ -66,49 +68,52 @@ func (e *endpoint) String() string { func (e *endpoint) NodeID() uint32 { e.mu.RLock() defer e.mu.RUnlock() + return e.id } func (e *endpoint) Address() (address string) { e.mu.RLock() defer e.mu.RUnlock() + return e.address } func (e *endpoint) Location() string { e.mu.RLock() defer e.mu.RUnlock() + return e.location } func (e *endpoint) LocalDC() bool { e.mu.RLock() defer e.mu.RUnlock() + return e.local } func (e *endpoint) LoadFactor() float32 { e.mu.RLock() defer e.mu.RUnlock() + return e.loadFactor } func (e *endpoint) LastUpdated() time.Time { e.mu.RLock() defer e.mu.RUnlock() + return e.lastUpdated } 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) + } } } @@ -155,10 +160,11 @@ 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) } } + return e } diff --git a/internal/grpcwrapper/rawoptional/rawoptional.go b/internal/grpcwrapper/rawoptional/rawoptional.go index aed976dfe..e4aec2c4a 100644 --- a/internal/grpcwrapper/rawoptional/rawoptional.go +++ b/internal/grpcwrapper/rawoptional/rawoptional.go @@ -18,6 +18,7 @@ func (b *Bool) ToProto() *bool { } val := b.Value + return &val } @@ -30,6 +31,7 @@ func (v *Duration) ToProto() *durationpb.Duration { if v.HasValue { return durationpb.New(v.Value) } + return nil } @@ -44,6 +46,7 @@ func (v *Int64) ToProto() *int64 { } val := v.Value + return &val } @@ -56,6 +59,7 @@ func (v *Time) ToProto() *timestamppb.Timestamp { if v.HasValue { return timestamppb.New(v.Value) } + return nil } @@ -63,6 +67,7 @@ func (v *Time) MustFromProto(proto *timestamppb.Timestamp) { if proto == nil { v.Value = time.Time{} v.HasValue = false + return } diff --git a/internal/grpcwrapper/rawscheme/entry.go b/internal/grpcwrapper/rawscheme/entry.go index e1516e6c3..f1fb68bde 100644 --- a/internal/grpcwrapper/rawscheme/entry.go +++ b/internal/grpcwrapper/rawscheme/entry.go @@ -26,25 +26,26 @@ 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 } @@ -73,7 +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/client.go b/internal/grpcwrapper/rawtopic/client.go index d5081bf80..5ccc30225 100644 --- a/internal/grpcwrapper/rawtopic/client.go +++ b/internal/grpcwrapper/rawtopic/client.go @@ -25,6 +25,7 @@ func (c *Client) AlterTopic(ctx context.Context, req *AlterTopicRequest) (res Al return res, xerrors.WithStackTrace(fmt.Errorf("ydb: alter topic grpc failed: %w", err)) } err = res.FromProto(resp) + return res, err } @@ -37,6 +38,7 @@ func (c *Client) CreateTopic( return res, xerrors.WithStackTrace(fmt.Errorf("ydb: create topic grpc failed: %w", err)) } err = res.FromProto(resp) + return res, err } @@ -48,6 +50,7 @@ func (c *Client) DescribeTopic(ctx context.Context, req DescribeTopicRequest) (r )) } err = res.FromProto(resp) + return res, err } @@ -60,6 +63,7 @@ func (c *Client) DropTopic( return res, xerrors.WithStackTrace(fmt.Errorf("ydb: drop topic grpc failed: %w", err)) } err = res.FromProto(resp) + return res, err } @@ -72,6 +76,7 @@ func (c *Client) StreamRead(ctxStreamLifeTime context.Context) (rawtopicreader.S ), ) } + return rawtopicreader.StreamReader{Stream: protoResp}, nil } @@ -84,5 +89,6 @@ func (c *Client) StreamWrite(ctxStreamLifeTime context.Context) (*rawtopicwriter ), ) } + return &rawtopicwriter.StreamWriter{Stream: protoResp}, nil } diff --git a/internal/grpcwrapper/rawtopic/controlplane_types.go b/internal/grpcwrapper/rawtopic/controlplane_types.go index 8dfe43a6f..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,9 @@ 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 bca36dab5..a1c3d4838 100644 --- a/internal/grpcwrapper/rawtopic/describe_topic.go +++ b/internal/grpcwrapper/rawtopic/describe_topic.go @@ -42,8 +42,8 @@ type DescribeTopicResult struct { } func (res *DescribeTopicResult) FromProto(protoResponse *Ydb_Topic.DescribeTopicResponse) error { - if err := res.Operation.FromProtoWithStatusCheck(protoResponse.Operation); err != nil { - return nil + if err := res.Operation.FromProtoWithStatusCheck(protoResponse.GetOperation()); err != nil { + return err } protoResult := &Ydb_Topic.DescribeTopicResult{} @@ -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 70f622403..166818331 100644 --- a/internal/grpcwrapper/rawtopic/rawtopiccommon/codec.go +++ b/internal/grpcwrapper/rawtopic/rawtopiccommon/codec.go @@ -53,12 +53,14 @@ func (c *SupportedCodecs) Contains(need Codec) bool { return true } } + return false } func (c *SupportedCodecs) Clone() SupportedCodecs { res := make(SupportedCodecs, len(*c)) copy(res, *c) + return res } @@ -77,6 +79,7 @@ func (c *SupportedCodecs) IsEqualsTo(other SupportedCodecs) bool { return false } } + return true } @@ -88,13 +91,14 @@ func (c *SupportedCodecs) ToProto() *Ydb_Topic.SupportedCodecs { for i := range codecs { proto.Codecs[i] = int32(codecs[i].ToProto().Number()) } + return proto } 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 a29d48f80..ad60427fa 100644 --- a/internal/grpcwrapper/rawtopic/rawtopicreader/messages.go +++ b/internal/grpcwrapper/rawtopic/rawtopicreader/messages.go @@ -72,8 +72,10 @@ func (offset OptionalOffset) ToInt64() int64 { func (offset OptionalOffset) ToInt64Pointer() *int64 { if offset.HasValue { v := offset.Offset.ToInt64() + return &v } + return nil } @@ -88,11 +90,11 @@ type UpdateTokenRequest struct { } type UpdateTokenResponse struct { + rawtopiccommon.UpdateTokenResponse + serverMessageImpl rawtopiccommon.ServerMessageMetadata - - rawtopiccommon.UpdateTokenResponse } // @@ -137,6 +139,7 @@ func (r *InitRequest) GetTopics() []string { for i := range res { res[i] = r.TopicsReadSettings[i].Path } + return res } @@ -157,7 +160,7 @@ type InitResponse struct { } func (g *InitResponse) fromProto(p *Ydb_Topic.StreamReadMessage_InitResponse) { - g.SessionID = p.SessionId + g.SessionID = p.GetSessionId() } // @@ -198,6 +201,7 @@ func (r *ReadResponse) GetPartitionBatchMessagesCounts() (partitionDataCount, ba messagesCount += len(partitionData.Batches[batchIndex].MessageData) } } + return partitionDataCount, batchCount, messagesCount } @@ -205,58 +209,59 @@ 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())], }) } } } } } + return nil } @@ -314,6 +319,7 @@ func (r *CommitOffsetRequest) toProto() *Ydb_Topic.StreamReadMessage_CommitOffse dstCommitOffset.Offsets[offsetIndex] = srcPartitionCommitOffset.Offsets[offsetIndex].ToProto() } } + return res } @@ -332,8 +338,9 @@ 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 } @@ -353,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 @@ -407,6 +414,7 @@ func (r *PartitionSessionStatusResponse) fromProto( return err } r.WriteTimeHighWatermark = p.GetWriteTimeHighWatermark().AsTime() + return nil } @@ -429,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 { @@ -461,6 +469,7 @@ func (r *StartPartitionSessionResponse) toProto() *Ydb_Topic.StreamReadMessage_S ReadOffset: r.ReadOffset.ToInt64Pointer(), CommitOffset: r.CommitOffset.ToInt64Pointer(), } + return res } @@ -482,9 +491,10 @@ 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 12124f1de..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,8 +31,14 @@ 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 { - err = xerrors.Transport(err) + if !xerrors.IsErrorFromServer(err) { + err = xerrors.Transport(err) + } + return nil, err } @@ -43,11 +50,12 @@ 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 resp.fromProto(m.InitResponse) + return resp, nil case *Ydb_Topic.StreamReadMessage_FromServer_ReadResponse: resp := &ReadResponse{} @@ -55,6 +63,7 @@ func (s StreamReader) Recv() (ServerMessage, error) { if err = resp.fromProto(m.ReadResponse); err != nil { return nil, err } + return resp, nil case *Ydb_Topic.StreamReadMessage_FromServer_StartPartitionSessionRequest: resp := &StartPartitionSessionRequest{} @@ -62,6 +71,7 @@ func (s StreamReader) Recv() (ServerMessage, error) { if err = resp.fromProto(m.StartPartitionSessionRequest); err != nil { return nil, err } + return resp, nil case *Ydb_Topic.StreamReadMessage_FromServer_StopPartitionSessionRequest: req := &StopPartitionSessionRequest{} @@ -69,6 +79,7 @@ func (s StreamReader) Recv() (ServerMessage, error) { if err = req.fromProto(m.StopPartitionSessionRequest); err != nil { return nil, err } + return req, nil case *Ydb_Topic.StreamReadMessage_FromServer_CommitOffsetResponse: resp := &CommitOffsetResponse{} @@ -76,6 +87,7 @@ func (s StreamReader) Recv() (ServerMessage, error) { if err = resp.fromProto(m.CommitOffsetResponse); err != nil { return nil, err } + return resp, nil case *Ydb_Topic.StreamReadMessage_FromServer_PartitionSessionStatusResponse: resp := &PartitionSessionStatusResponse{} @@ -83,16 +95,18 @@ func (s StreamReader) Recv() (ServerMessage, error) { if err = resp.fromProto(m.PartitionSessionStatusResponse); err != nil { return nil, err } + return resp, nil case *Ydb_Topic.StreamReadMessage_FromServer_UpdateTokenResponse: resp := &UpdateTokenResponse{} resp.ServerMessageMetadata = meta resp.MustFromProto(m.UpdateTokenResponse) + return resp, nil default: return nil, xerrors.WithStackTrace(fmt.Errorf( "ydb: receive unexpected message (%v): %w", - reflect.TypeOf(grpcMess.ServerMessage), + reflect.TypeOf(grpcMess.GetServerMessage()), ErrUnexpectedMessageType, )) } @@ -107,11 +121,13 @@ func (s StreamReader) Send(msg ClientMessage) (err error) { grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ ClientMessage: &Ydb_Topic.StreamReadMessage_FromClient_InitRequest{InitRequest: m.toProto()}, } + return s.Stream.Send(grpcMess) case *ReadRequest: grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ ClientMessage: &Ydb_Topic.StreamReadMessage_FromClient_ReadRequest{ReadRequest: m.toProto()}, } + return s.Stream.Send(grpcMess) case *StartPartitionSessionResponse: grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ @@ -119,6 +135,7 @@ func (s StreamReader) Send(msg ClientMessage) (err error) { StartPartitionSessionResponse: m.toProto(), }, } + return s.Stream.Send(grpcMess) case *StopPartitionSessionResponse: grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ @@ -126,6 +143,7 @@ func (s StreamReader) Send(msg ClientMessage) (err error) { StopPartitionSessionResponse: m.toProto(), }, } + return s.Stream.Send(grpcMess) case *CommitOffsetRequest: grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ @@ -133,6 +151,7 @@ func (s StreamReader) Send(msg ClientMessage) (err error) { CommitOffsetRequest: m.toProto(), }, } + return s.Stream.Send(grpcMess) case *PartitionSessionStatusRequest: grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ @@ -140,6 +159,7 @@ func (s StreamReader) Send(msg ClientMessage) (err error) { PartitionSessionStatusRequest: m.toProto(), }, } + return s.Stream.Send(grpcMess) case *UpdateTokenRequest: grpcMess := &Ydb_Topic.StreamReadMessage_FromClient{ @@ -147,6 +167,7 @@ func (s StreamReader) Send(msg ClientMessage) (err error) { UpdateTokenRequest: m.ToProto(), }, } + return s.Stream.Send(grpcMess) default: return xerrors.WithStackTrace(fmt.Errorf("ydb: send unexpected message type: %v", reflect.TypeOf(msg))) diff --git a/internal/grpcwrapper/rawtopic/rawtopicwriter/messages.go b/internal/grpcwrapper/rawtopic/rawtopicwriter/messages.go index c943506fa..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,14 +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 - return r.WriteStatistics.fromProto(response.WriteStatistics) + r.PartitionID = response.GetPartitionId() + + return r.WriteStatistics.fromProto(response.GetWriteStatistics()) } type WriteAck struct { @@ -229,8 +230,9 @@ func (wa *WriteAck) fromProto(pb *Ydb_Topic.StreamWriteMessage_WriteResponse_Wri if pb == nil { return xerrors.WithStackTrace(errWriteResultResponseWriteAckIsNil) } - wa.SeqNo = pb.SeqNo - return wa.MessageWriteStatus.fromProto(pb.MessageWriteStatus) + wa.SeqNo = pb.GetSeqNo() + + return wa.MessageWriteStatus.fromProto(pb.GetMessageWriteStatus()) } // MessageWriteStatus is struct because it included in per-message structure and @@ -246,11 +248,13 @@ 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: return xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf("ydb: unexpected write status type: %v", reflect.TypeOf(v)))) @@ -284,10 +288,11 @@ 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 } @@ -298,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 3b62de655..e2689df5a 100644 --- a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go +++ b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter.go @@ -41,7 +41,10 @@ func (w *StreamWriter) Recv() (ServerMessage, error) { grpcMsg, err := w.Stream.Recv() if err != nil { - err = xerrors.Transport(err) + if !xerrors.IsErrorFromServer(err) { + err = xerrors.Transport(err) + } + return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf( "ydb: failed to read grpc message from writer stream: %w", err, @@ -56,11 +59,12 @@ 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 res.mustFromProto(v.InitResponse) + return &res, nil case *Ydb_Topic.StreamWriteMessage_FromServer_WriteResponse: var res WriteResult @@ -69,10 +73,12 @@ func (w *StreamWriter) Recv() (ServerMessage, error) { if err != nil { return nil, err } + return &res, nil case *Ydb_Topic.StreamWriteMessage_FromServer_UpdateTokenResponse: var res UpdateTokenResponse res.MustFromProto(v.UpdateTokenResponse) + return &res, nil default: return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf( @@ -104,6 +110,7 @@ func (w *StreamWriter) Send(rawMsg ClientMessage) (err error) { if writeErr != nil { return writeErr } + return sendWriteRequest(w.Stream.Send, writeReqProto) case *UpdateTokenRequest: protoMsg.ClientMessage = &Ydb_Topic.StreamWriteMessage_FromClient_UpdateTokenRequest{ @@ -120,6 +127,7 @@ func (w *StreamWriter) Send(rawMsg ClientMessage) (err error) { if err != nil { return xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf("ydb: failed to send grpc message to writer stream: %w", err))) } + return nil } @@ -164,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 } @@ -182,5 +190,6 @@ func sendWriteRequest(send sendFunc, req *Ydb_Topic.StreamWriteMessage_FromClien } req.WriteRequest.Messages = lastMessages + return sendWriteRequest(send, req) } diff --git a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go index 4233529d7..f076adab9 100644 --- a/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go +++ b/internal/grpcwrapper/rawtopic/rawtopicwriter/streamwriter_test.go @@ -25,7 +25,8 @@ 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 } err := sendWriteRequest(send, expected) @@ -71,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 @@ -81,6 +82,7 @@ func TestSendWriteRequest(t *testing.T) { switch sendCounter { case 1: require.Equal(t, originalMessage, req) + return grpcStatus.Error(codes.ResourceExhausted, "test resource exhausted") case 2: require.Equal(t, split1, req) @@ -89,6 +91,7 @@ func TestSendWriteRequest(t *testing.T) { default: t.Fatal() } + return nil } diff --git a/internal/grpcwrapper/rawydb/issues.go b/internal/grpcwrapper/rawydb/issues.go index 2c04ce799..09ed1b3e3 100644 --- a/internal/grpcwrapper/rawydb/issues.go +++ b/internal/grpcwrapper/rawydb/issues.go @@ -17,9 +17,10 @@ func (issuesPointer *Issues) FromProto(p []*Ydb_Issue.IssueMessage) error { issues := *issuesPointer for i := range issues { if err := issues[i].FromProto(p[i]); err != nil { - return nil + return err } } + return nil } @@ -29,6 +30,7 @@ func (issuesPointer *Issues) String() string { for i := range issues { issuesStrings[i] = issues[i].String() } + return strings.Join(issuesStrings, ", ") } diff --git a/internal/grpcwrapper/rawydb/operation.go b/internal/grpcwrapper/rawydb/operation.go index 3c6dcafca..5073cb8aa 100644 --- a/internal/grpcwrapper/rawydb/operation.go +++ b/internal/grpcwrapper/rawydb/operation.go @@ -21,13 +21,15 @@ func (o *Operation) FromProto(proto *Ydb_Operations.Operation) error { if err := o.Status.FromProto(proto.GetStatus()); err != nil { return err } - return o.Issues.FromProto(proto.Issues) + + return o.Issues.FromProto(proto.GetIssues()) } func (o *Operation) OperationStatusToError() error { if !o.Status.IsSuccess() { return xerrors.WithStackTrace(fmt.Errorf("ydb: create topic error [%v]: %v", o.Status, o.Issues)) } + return nil } @@ -35,5 +37,6 @@ func (o *Operation) FromProtoWithStatusCheck(proto *Ydb_Operations.Operation) er if err := o.FromProto(proto); err != nil { return err } + return o.OperationStatusToError() } diff --git a/internal/grpcwrapper/rawydb/operation_params.go b/internal/grpcwrapper/rawydb/operation_params.go index b2fc9aeba..21d80eba1 100644 --- a/internal/grpcwrapper/rawydb/operation_params.go +++ b/internal/grpcwrapper/rawydb/operation_params.go @@ -19,6 +19,7 @@ func (p *OperationParams) ToProto() *Ydb_Operations.OperationParams { } res.OperationTimeout = p.OperationTimeout.ToProto() res.CancelAfter = p.CancelAfter.ToProto() + return res } diff --git a/internal/grpcwrapper/rawydb/status.go b/internal/grpcwrapper/rawydb/status.go index f31482ac7..e6263b27c 100644 --- a/internal/grpcwrapper/rawydb/status.go +++ b/internal/grpcwrapper/rawydb/status.go @@ -11,6 +11,7 @@ const ( func (s *StatusCode) FromProto(p Ydb.StatusIds_StatusCode) error { *s = StatusCode(p) + return nil } diff --git a/internal/meta/context.go b/internal/meta/context.go index 2df7010c8..b3a410dcc 100644 --- a/internal/meta/context.go +++ b/internal/meta/context.go @@ -11,6 +11,7 @@ func WithTraceID(ctx context.Context, traceID string) context.Context { if md, has := metadata.FromOutgoingContext(ctx); !has || len(md[HeaderTraceID]) == 0 { return metadata.AppendToOutgoingContext(ctx, HeaderTraceID, traceID) } + return ctx } @@ -18,12 +19,19 @@ func traceID(ctx context.Context) (string, bool) { if md, has := metadata.FromOutgoingContext(ctx); has && len(md[HeaderTraceID]) > 0 { return md[HeaderTraceID][0], true } + 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 @@ -32,10 +40,11 @@ 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 { +func WithAllowFeatures(ctx context.Context, features ...string) context.Context { kv := make([]string, 0, len(features)*2) for _, feature := range features { kv = append(kv, HeaderClientCapabilities, feature) } + return metadata.AppendToOutgoingContext(ctx, kv...) } 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/incoming.go b/internal/meta/incoming.go index c4e95e384..940547d2a 100644 --- a/internal/meta/incoming.go +++ b/internal/meta/incoming.go @@ -20,6 +20,7 @@ func WithTrailerCallback(ctx context.Context, callback MetadataCallback) context }, )) } + return context.WithValue(ctx, metadataCallbackKey{}, callback) } diff --git a/internal/meta/meta.go b/internal/meta/meta.go index fbe4f4603..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,23 +24,25 @@ 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) } } + return m } 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 } } @@ -66,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) { @@ -80,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) } @@ -94,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 { @@ -108,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) }() @@ -118,6 +129,7 @@ func (m *Meta) meta(ctx context.Context) (_ metadata.MD, err error) { if stringer, ok := m.credentials.(fmt.Stringer); ok { return nil, xerrors.WithStackTrace(fmt.Errorf("%w: %s", err, stringer.String())) } + return nil, xerrors.WithStackTrace(err) } @@ -131,5 +143,6 @@ func (m *Meta) Context(ctx context.Context) (_ context.Context, err error) { if err != nil { return ctx, xerrors.WithStackTrace(err) } + return metadata.NewOutgoingContext(ctx, md), nil } 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 e93792c35..182c57dac 100644 --- a/internal/meta/trace_id.go +++ b/internal/meta/trace_id.go @@ -19,12 +19,15 @@ 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 { return ctx, "", xerrors.WithStackTrace(err) } id := uuid.String() + return metadata.AppendToOutgoingContext(ctx, HeaderTraceID, id), id, nil } diff --git a/internal/mock/conn.go b/internal/mock/conn.go index 1b9a66523..ac57f6c41 100644 --- a/internal/mock/conn.go +++ b/internal/mock/conn.go @@ -67,11 +67,13 @@ func (c *Conn) GetState() conn.State { func (c *Conn) SetState(ctx context.Context, state conn.State) conn.State { c.State = state + return c.State } func (c *Conn) Unban(ctx context.Context) conn.State { c.SetState(ctx, conn.Online) + return conn.Online } @@ -115,6 +117,7 @@ func (e *Endpoint) String() string { func (e *Endpoint) Copy() endpoint.Endpoint { c := *e + return &c } diff --git a/internal/operation/context.go b/internal/operation/context.go index b82d505b4..3ce2e1ee5 100644 --- a/internal/operation/context.go +++ b/internal/operation/context.go @@ -17,6 +17,7 @@ func WithTimeout(ctx context.Context, operationTimeout time.Duration) context.Co // The current cancelation timeout is already smaller than the new one. return ctx } + return context.WithValue(ctx, ctxOperationTimeoutKey{}, operationTimeout) } @@ -28,6 +29,7 @@ func WithCancelAfter(ctx context.Context, operationCancelAfter time.Duration) co // The current cancelation timeout is already smaller than the new one. return ctx } + return context.WithValue(ctx, ctxOperationCancelAfterKey{}, operationCancelAfter) } @@ -35,6 +37,7 @@ func WithCancelAfter(ctx context.Context, operationCancelAfter time.Duration) co // YDB should try to cancel operation and return result regardless of the cancelation. func Timeout(ctx context.Context) (d time.Duration, ok bool) { d, ok = ctx.Value(ctxOperationTimeoutKey{}).(time.Duration) + return } @@ -42,6 +45,7 @@ func Timeout(ctx context.Context) (d time.Duration, ok bool) { // YDB should try to cancel operation and return result regardless of the cancellation. func CancelAfter(ctx context.Context) (d time.Duration, ok bool) { d, ok = ctx.Value(ctxOperationCancelAfterKey{}).(time.Duration) + return } @@ -50,5 +54,6 @@ func untilDeadline(ctx context.Context) (time.Duration, bool) { if ok { return time.Until(deadline), true } + return 0, false } diff --git a/internal/operation/params.go b/internal/operation/params.go index 2ca1c3c01..f45e941f8 100644 --- a/internal/operation/params.go +++ b/internal/operation/params.go @@ -25,6 +25,7 @@ func Params( if timeout == 0 && cancelAfter == 0 && mode == 0 { return nil } + return &Ydb_Operations.OperationParams{ OperationMode: mode.toYDB(), OperationTimeout: timeoutParam(timeout), diff --git a/internal/operation/params_test.go b/internal/operation/params_test.go index 256dd1cb9..605453279 100644 --- a/internal/operation/params_test.go +++ b/internal/operation/params_test.go @@ -224,6 +224,7 @@ func TestParams(t *testing.T) { ), time.Second*5, ), time.Second*10) + return ctx }(), timeout: time.Second * 2, @@ -244,6 +245,7 @@ func TestParams(t *testing.T) { ), time.Second*5, ), time.Second*1) + return ctx }(), timeout: time.Second * 2, @@ -264,6 +266,7 @@ func TestParams(t *testing.T) { ), time.Second*5, ), time.Second*1) + return ctx }(), preferContextTimeout: true, @@ -279,6 +282,7 @@ func TestParams(t *testing.T) { { ctx: func() context.Context { ctx, _ := xcontext.WithTimeout(context.Background(), time.Second*1) + return ctx }(), preferContextTimeout: true, @@ -306,27 +310,28 @@ func TestParams(t *testing.T) { tt.exp, ) } + 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/operation/status.go b/internal/operation/status.go new file mode 100644 index 000000000..4b57ff53d --- /dev/null +++ b/internal/operation/status.go @@ -0,0 +1,11 @@ +package operation + +import ( + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Issue" +) + +type Status interface { + GetStatus() Ydb.StatusIds_StatusCode + GetIssues() []*Ydb_Issue.IssueMessage +} diff --git a/internal/operation/timeout.go b/internal/operation/timeout.go index dc62bdddb..a29dbe76b 100644 --- a/internal/operation/timeout.go +++ b/internal/operation/timeout.go @@ -10,5 +10,6 @@ func timeoutParam(d time.Duration) *durationpb.Duration { if d > 0 { return durationpb.New(d) } + return nil } 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..b1d004cbf --- /dev/null +++ b/internal/params/builder_test.go @@ -0,0 +1,441 @@ +package params + +import ( + "encoding/json" + "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 paramsToJSON(params map[string]*Ydb.TypedValue) string { + b, _ := json.MarshalIndent(params, "", "\t") //nolint:errchkjson + + return string(b) +} + +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, + paramsToJSON( + map[string]*Ydb.TypedValue{ + "$x": { + Type: tc.expected.Type, + Value: tc.expected.Value, + }, + }), + paramsToJSON(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..2fed82d33 --- /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, paramsToJSON( + 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, + }, + }, + }, + }, + }), paramsToJSON(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, paramsToJSON( + 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, + }, + }, + }, + }, + }, + }, + }), paramsToJSON(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..153212f89 --- /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, paramsToJSON( + 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, + }, + }, + }, + }), paramsToJSON(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, paramsToJSON( + 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, + }, + }, + }, + }, + }, + }), paramsToJSON(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..37246e364 --- /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, paramsToJSON( + map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_OptionalType{ + OptionalType: &Ydb.OptionalType{ + Item: tc.expected.Type, + }, + }, + }, + Value: tc.expected.Value, + }, + }), paramsToJSON(params)) + }) + } +} diff --git a/internal/params/parameters.go b/internal/params/parameters.go new file mode 100644 index 000000000..7faa23041 --- /dev/null +++ b/internal/params/parameters.go @@ -0,0 +1,325 @@ +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) 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..4fc6f15f1 --- /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, paramsToJSON( + map[string]*Ydb.TypedValue{ + "$x": { + Type: tc.expected.Type, + Value: tc.expected.Value, + }, + }), paramsToJSON(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..0a97b866b --- /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, paramsToJSON( + 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{}, + }, + }, + }, + }, + }, + }), paramsToJSON(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, paramsToJSON( + 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{}, + }, + }, + }, + }, + }, + }), paramsToJSON(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..d4aac1775 --- /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, paramsToJSON(tt.params), paramsToJSON(params)) + }) + } +} diff --git a/internal/params/tuple.go b/internal/params/tuple.go new file mode 100644 index 000000000..0762cdb05 --- /dev/null +++ b/internal/params/tuple.go @@ -0,0 +1,172 @@ +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 +} diff --git a/internal/params/tuple_test.go b/internal/params/tuple_test.go new file mode 100644 index 000000000..9938fae0c --- /dev/null +++ b/internal/params/tuple_test.go @@ -0,0 +1,735 @@ +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) { + for _, tt := range []struct { + name string + builder Builder + params map[string]*Ydb.TypedValue + }{ + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint64(123).EndTuple(), + params: 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, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint64Value{ + Uint64Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int64(123).EndTuple(), + params: 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_INT64, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int64Value{ + Int64Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint32(123).EndTuple(), + params: 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_UINT32, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int32(123).EndTuple(), + params: 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_INT32, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int32Value{ + Int32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint16(123).EndTuple(), + params: 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_UINT16, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int16(123).EndTuple(), + params: 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_INT16, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int32Value{ + Int32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Uint8(123).EndTuple(), + params: 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_UINT8, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Int8(123).EndTuple(), + params: 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_INT8, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int32Value{ + Int32Value: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Bool(true).EndTuple(), + params: 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_BOOL, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_BoolValue{ + BoolValue: true, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Text("test").EndTuple(), + params: 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_UTF8, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_TextValue{ + TextValue: "test", + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Bytes([]byte("test")).EndTuple(), + params: 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_STRING, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_BytesValue{ + BytesValue: []byte("test"), + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Float(123).EndTuple(), + params: 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_FLOAT, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_FloatValue{ + FloatValue: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Double(123).EndTuple(), + params: 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_DOUBLE, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_DoubleValue{ + DoubleValue: 123, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Interval(time.Second).EndTuple(), + params: 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_INTERVAL, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Int64Value{ + Int64Value: 1000000, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Datetime(time.Unix(123456789, 456)).EndTuple(), + params: 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_DATETIME, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 123456789, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Date(time.Unix(123456789, 456)).EndTuple(), + params: 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_DATE, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint32Value{ + Uint32Value: 1428, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Timestamp(time.Unix(123456789, 456)).EndTuple(), + params: 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_TIMESTAMP, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_Uint64Value{ + Uint64Value: 123456789000000, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().Decimal([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}, 22, 9).EndTuple(), //nolint:lll + params: map[string]*Ydb.TypedValue{ + "$x": { + Type: &Ydb.Type{ + Type: &Ydb.Type_TupleType{ + TupleType: &Ydb.TupleType{ + Elements: []*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").BeginTuple().Add().JSON(`{"a": 1,"b": "B"}`).EndTuple(), + params: 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_JSON, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_TextValue{ + TextValue: `{"a": 1,"b": "B"}`, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().JSONDocument(`{"a": 1,"b": "B"}`).EndTuple(), + params: 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_JSON_DOCUMENT, + }, + }, + }, + }, + }, + }, + Value: &Ydb.Value{ + Items: []*Ydb.Value{ + { + Value: &Ydb.Value_TextValue{ + TextValue: `{"a": 1,"b": "B"}`, + }, + }, + }, + }, + }, + }, + }, + { + name: xtest.CurrentFileLine(), + builder: Builder{}.Param("$x").BeginTuple().Add().YSON([]byte(`[ 1; 2; 3; 4; 5 ]`)).EndTuple(), + params: 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_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").BeginTuple().Add(). + UUID([...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}).EndTuple(), + params: 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_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").BeginTuple().AddItems(value.Uint64Value(123), value.Uint64Value(321)).EndTuple(), + params: 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, + }, + }, + }, + }, + }, + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + a := allocator.New() + defer a.Free() + params := tt.builder.Build().ToYDB(a) + require.Equal(t, paramsToJSON(tt.params), paramsToJSON(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..4089a5465 --- /dev/null +++ b/internal/query/execute_query_test.go @@ -0,0 +1,1073 @@ +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" +) + +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, + 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: "WithoutOptions", + }, + }, + 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, + 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: "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, + 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: "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, + 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: "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, + 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: "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, + 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: "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, + 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: "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, + 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: "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, + 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: "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..f6e6f15d5 --- /dev/null +++ b/internal/query/tx/control.go @@ -0,0 +1,165 @@ +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 NewControl( + BeginTx(WithSerializableReadWrite()), + CommitTx(), + ) +} + +// 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 8705d7897..eec07b118 100644 --- a/internal/ratelimiter/client.go +++ b/internal/ratelimiter/client.go @@ -33,14 +33,15 @@ func (c *Client) Close(ctx context.Context) error { if c == nil { return xerrors.WithStackTrace(errNilClient) } + 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( @@ -57,6 +58,7 @@ func (c *Client) CreateResource( if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -87,6 +89,7 @@ func (c *Client) createResource( operation.ModeSync, ), }) + return } @@ -104,6 +107,7 @@ func (c *Client) AlterResource( if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -134,6 +138,7 @@ func (c *Client) alterResource( operation.ModeSync, ), }) + return } @@ -151,6 +156,7 @@ func (c *Client) DropResource( if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -173,6 +179,7 @@ func (c *Client) dropResource( operation.ModeSync, ), }) + return } @@ -187,10 +194,12 @@ func (c *Client) ListResource( } call := func(ctx context.Context) (err error) { list, err = c.listResource(ctx, coordinationNodePath, resourcePath, recursive) + return xerrors.WithStackTrace(err) } if !c.config.AutoRetry() { err := call(ctx) + return list, err } err := retry.Retry(ctx, call, @@ -198,6 +207,7 @@ func (c *Client) ListResource( retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), ) + return list, err } @@ -229,6 +239,7 @@ func (c *Client) listResource( if err != nil { return nil, xerrors.WithStackTrace(err) } + return result.GetResourcePaths(), nil } @@ -242,10 +253,12 @@ func (c *Client) DescribeResource( } call := func(ctx context.Context) error { resource, err = c.describeResource(ctx, coordinationNodePath, resourcePath) + return xerrors.WithStackTrace(err) } if !c.config.AutoRetry() { err = call(ctx) + return } err = retry.Retry(ctx, call, @@ -253,6 +266,7 @@ func (c *Client) DescribeResource( retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), ) + return } @@ -315,6 +329,7 @@ func (c *Client) AcquireResource( if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), diff --git a/internal/ratelimiter/config/config.go b/internal/ratelimiter/config/config.go index 8f9aca3c7..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,10 +37,11 @@ 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) } } + return c } diff --git a/internal/ratelimiter/errors/errors.go b/internal/ratelimiter/errors/errors.go index 277b93371..761cf25c7 100644 --- a/internal/ratelimiter/errors/errors.go +++ b/internal/ratelimiter/errors/errors.go @@ -33,6 +33,7 @@ func NewAcquire(amoount uint64, err error) ratelimiter.AcquireError { func IsAcquireError(err error) bool { var ae *acquireError + return xerrors.As(err, &ae) } @@ -41,5 +42,6 @@ func ToAcquireError(err error) ratelimiter.AcquireError { if xerrors.As(err, &ae) { return ae } + return nil } diff --git a/internal/ratelimiter/options/acquire.go b/internal/ratelimiter/options/acquire.go index 395ccf8d3..1febf3e63 100644 --- a/internal/ratelimiter/options/acquire.go +++ b/internal/ratelimiter/options/acquire.go @@ -74,10 +74,11 @@ 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) } } + return h } diff --git a/internal/repeater/repeater.go b/internal/repeater/repeater.go index bb24c2f2c..8cb2fb0c0 100644 --- a/internal/repeater/repeater.go +++ b/internal/repeater/repeater.go @@ -78,6 +78,7 @@ func EventType(ctx context.Context) Event { if eventType, ok := ctx.Value(ctxEventTypeKey{}).(Event); ok { return eventType } + return EventUnknown } @@ -107,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) } } @@ -146,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/repeater/repeater_test.go b/internal/repeater/repeater_test.go index c6af53a58..6749ac19a 100644 --- a/internal/repeater/repeater_test.go +++ b/internal/repeater/repeater_test.go @@ -20,6 +20,7 @@ func TestRepeaterNoWakeUpsAfterStop(t *testing.T) { r := New(context.Background(), interval, func(ctx context.Context) (err error) { wakeUpStart <- struct{}{} <-wakeUpDone + return nil }, WithClock(fakeClock)) @@ -83,6 +84,7 @@ func TestRepeaterForceLogBackoff(t *testing.T) { } lastWakeUp = fakeClock.Now() wakeUps++ + return fmt.Errorf("special error for force with log backoff") }, WithClock(fakeClock)) defer r.Stop() 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 22428fe41..6f46ed736 100644 --- a/internal/scheme/client.go +++ b/internal/scheme/client.go @@ -34,19 +34,20 @@ func (c *Client) Close(_ context.Context) error { if c == nil { return xerrors.WithStackTrace(errNilClient) } + 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() { @@ -58,6 +59,7 @@ func (c *Client) MakeDirectory(ctx context.Context, path string) (finalErr error if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -78,12 +80,13 @@ func (c *Client) makeDirectory(ctx context.Context, path string) (err error) { ), }, ) + return xerrors.WithStackTrace(err) } 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() { @@ -95,6 +98,7 @@ func (c *Client) RemoveDirectory(ctx context.Context, path string) (finalErr err if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -115,20 +119,25 @@ func (c *Client) removeDirectory(ctx context.Context, path string) (err error) { ), }, ) + return xerrors.WithStackTrace(err) } 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) }() call := func(ctx context.Context) (err error) { d, err = c.listDirectory(ctx, path) + return xerrors.WithStackTrace(err) } if !c.config.AutoRetry() { err := call(ctx) + return d, xerrors.WithStackTrace(err) } err := retry.Retry(ctx, call, @@ -136,6 +145,7 @@ func (c *Client) ListDirectory(ctx context.Context, path string) (d scheme.Direc retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), ) + return d, xerrors.WithStackTrace(err) } @@ -165,15 +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() { @@ -184,10 +195,12 @@ func (c *Client) DescribePath(ctx context.Context, path string) (e scheme.Entry, if err != nil { return xerrors.WithStackTrace(err) } + return nil } if !c.config.AutoRetry() { err := call(ctx) + return e, err } err := retry.Retry(ctx, call, @@ -195,6 +208,7 @@ func (c *Client) DescribePath(ctx context.Context, path string) (e scheme.Entry, retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), ) + return e, xerrors.WithStackTrace(err) } @@ -222,7 +236,8 @@ 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 } @@ -230,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 { @@ -248,6 +263,7 @@ func (c *Client) ModifyPermissions( if !c.config.AutoRetry() { return call(ctx) } + return retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithIdempotent(true), @@ -273,6 +289,7 @@ func (c *Client) modifyPermissions(ctx context.Context, path string, desc permis if err != nil { return xerrors.WithStackTrace(err) } + return nil } diff --git a/internal/scheme/config/config.go b/internal/scheme/config/config.go index ff3bd745a..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,10 +50,11 @@ 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) } } + return c } diff --git a/internal/scheme/helpers/check_exists.go b/internal/scheme/helpers/check_exists.go index 2765ab9c7..534d12b36 100644 --- a/internal/scheme/helpers/check_exists.go +++ b/internal/scheme/helpers/check_exists.go @@ -50,8 +50,10 @@ func IsDirectoryExists(ctx context.Context, c schemeClient, directory string) ( childDirectory, parentDirectory, t.String(), )) } + return true, nil } + return false, nil } @@ -92,10 +94,12 @@ func IsEntryExists(ctx context.Context, c schemeClient, absPath string, entryTyp return true, nil } } + return false, xerrors.WithStackTrace(fmt.Errorf( "entry type of '%s' (%s) in path '%s' is not corresponds to %v", entryName, childrenType, directory, entryTypes, )) } + return false, nil } diff --git a/internal/scheme/helpers/check_exists_test.go b/internal/scheme/helpers/check_exists_test.go index 8b3086f36..265346cae 100644 --- a/internal/scheme/helpers/check_exists_test.go +++ b/internal/scheme/helpers/check_exists_test.go @@ -33,6 +33,7 @@ func (c isDirectoryExistsSchemeClient) ListDirectory(ctx context.Context, path s } if strings.HasPrefix(c.existingPath, path) { children := strings.Split(strings.TrimLeft(c.existingPath, path), "/") + return scheme.Directory{ Entry: scheme.Entry{ Name: path, @@ -46,6 +47,7 @@ func (c isDirectoryExistsSchemeClient) ListDirectory(ctx context.Context, path s }, }, nil } + return d, fmt.Errorf("path '%s' not found in '%s'", path, c) } @@ -173,6 +175,7 @@ func (c isTableExistsSchemeClient) ListDirectory(ctx context.Context, path strin }, nil } } + return d, fmt.Errorf("path '%s' not found in '%s'", path, 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 b73f3c75f..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,48 +32,53 @@ 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) } if !c.config.AutoRetry() { err = call(ctx) + return } err = retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), ) + return r, xerrors.WithStackTrace(err) } 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(), @@ -97,6 +102,7 @@ func (c *Client) execute( if err != nil { return nil, xerrors.WithStackTrace(err) } + return scanner.NewUnary(result.GetResultSets(), result.GetQueryStats()), nil } @@ -121,10 +127,12 @@ func (c *Client) Explain( } call := func(ctx context.Context) error { e, err = c.explain(ctx, query, mode) + return xerrors.WithStackTrace(err) } if !c.config.AutoRetry() { err = call(ctx) + return } err = retry.Retry(ctx, call, @@ -132,6 +140,7 @@ func (c *Client) Explain( retry.WithIdempotent(true), retry.WithTrace(c.config.TraceRetry()), ) + return e, xerrors.WithStackTrace(err) } @@ -142,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{ @@ -177,48 +186,52 @@ 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 } 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) } call := func(ctx context.Context) error { r, err = c.streamExecute(ctx, query, params) + return xerrors.WithStackTrace(err) } if !c.config.AutoRetry() { err = call(ctx) + return } err = retry.Retry(ctx, call, retry.WithStackTrace(), retry.WithTrace(c.config.TraceRetry()), ) + return r, xerrors.WithStackTrace(err) } 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(), @@ -239,6 +252,7 @@ func (c *Client) streamExecute( stream, err := c.service.StreamExecuteYql(ctx, request) if err != nil { cancel() + return nil, xerrors.WithStackTrace(err) } @@ -261,12 +275,14 @@ func (c *Client) streamExecute( if result == nil || err != nil { return nil, nil, xerrors.WithStackTrace(err) } + return result.GetResultSet(), result.GetQueryStats(), nil } }, func(err error) error { cancel() onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err)) + return err }, ) @@ -276,16 +292,19 @@ 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) }() + 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 52fc846ae..35f90651d 100644 --- a/internal/scripting/config/config.go +++ b/internal/scripting/config/config.go @@ -36,10 +36,11 @@ 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) } } + return c } diff --git a/internal/secret/password.go b/internal/secret/password.go index 79a004984..2b6bdd1ba 100644 --- a/internal/secret/password.go +++ b/internal/secret/password.go @@ -19,5 +19,6 @@ func Password(password string) string { bytes[i] = '*' } } + return xstring.FromBytes(bytes) } diff --git a/internal/secret/token.go b/internal/secret/token.go index 70fab0eca..dc90e0c59 100644 --- a/internal/secret/token.go +++ b/internal/secret/token.go @@ -16,5 +16,6 @@ func Token(token string) string { mask.WriteString("****") } mask.WriteString(fmt.Sprintf("(CRC-32c: %08X)", crc32.Checksum([]byte(token), crc32.IEEETable))) + return mask.String() } 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 518551e5b..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,9 +12,10 @@ func (id functionID) FunctionID() string { return string(id) } -func FunctionID(id string) caller { +func FunctionID(id string) Caller { if id != "" { return functionID(id) } + return Call(1) } diff --git a/internal/stack/function_id_test.go b/internal/stack/function_id_test.go new file mode 100644 index 000000000..f1161fe3d --- /dev/null +++ b/internal/stack/function_id_test.go @@ -0,0 +1,45 @@ +package stack + +import ( + "testing" + + "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 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(), + ) + }) +} diff --git a/internal/stack/record.go b/internal/stack/record.go index c47097a5c..efe5e9db6 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 @@ -72,6 +72,7 @@ type call struct { func Call(depth int) (c call) { c.function, c.file, c.line, _ = runtime.Caller(depth + 1) + return c } @@ -86,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 ( @@ -102,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 { @@ -166,6 +170,7 @@ func (c call) Record(opts ...recordOption) string { buffer.WriteByte(')') } } + return buffer.String() } diff --git a/internal/table/client.go b/internal/table/client.go index 1a3fceeeb..20dd9cb6e 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, @@ -63,6 +66,7 @@ func newClient( waitChPool: sync.Pool{ New: func() interface{} { ch := make(chan *session) + return &ch }, }, @@ -73,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. @@ -120,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) } } @@ -164,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 @@ -177,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 @@ -217,6 +221,7 @@ func (c *Client) createSession(ctx context.Context, opts ...createSessionOption) if r.err != nil { return nil, xerrors.WithStackTrace(r.err) } + return r.s, nil } } @@ -234,6 +239,7 @@ func (c *Client) CreateSession(ctx context.Context, opts ...table.Option) (_ tab if err != nil { return nil, xerrors.WithStackTrace(err) } + return s, nil } if !c.config.AutoRetry() { @@ -241,6 +247,7 @@ func (c *Client) CreateSession(ctx context.Context, opts ...table.Option) (_ tab if err != nil { return nil, xerrors.WithStackTrace(err) } + return s, nil } err = retry.Retry(ctx, @@ -249,25 +256,27 @@ func (c *Client) CreateSession(ctx context.Context, opts ...table.Option) (_ tab if err != nil { return xerrors.WithStackTrace(err) } + return nil }, append( []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("")) - return func(info trace.RetryLoopIntermediateInfo) func(trace.RetryLoopDoneInfo) { - onDone := onIntermediate(info.Error) - return func(info trace.RetryLoopDoneInfo) { - onDone(s, info.Attempts, info.Error) - } + 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.RetryLoopDoneInfo) { + onDone(s, info.Attempts, info.Error) } }, }), }, c.retryOptions(opts...).RetryOptions..., )..., ) + return s, xerrors.WithStackTrace(err) } @@ -372,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) }() @@ -389,6 +400,7 @@ func (c *Client) internalPoolGet(ctx context.Context, opts ...getOption) (s *ses if c.nodeChecker != nil && !c.nodeChecker.HasNode(s.NodeID()) { _ = s.Close(ctx) s = nil + continue } @@ -437,6 +449,7 @@ func (c *Client) internalPoolGet(ctx context.Context, opts ...getOption) (s *ses idle = c.idle.Len() createInProgress = c.createInProgress }) + return s, xerrors.WithStackTrace( fmt.Errorf("failed to get session from pool ("+ "attempts: %d, latency: %v, pool have %d sessions (%d busy, %d idle, %d create_in_progress): %w", @@ -444,6 +457,7 @@ func (c *Client) internalPoolGet(ctx context.Context, opts ...getOption) (s *ses ), ) } + return s, nil } @@ -465,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) @@ -481,6 +497,7 @@ func (c *Client) internalPoolWaitFromCh(ctx context.Context, t *trace.Table) (s c.mu.WithLock(func() { c.waitQ.Remove(el) }) + return nil, xerrors.WithStackTrace(errClosedClient) case s, ok = <-*ch: @@ -496,18 +513,21 @@ func (c *Client) internalPoolWaitFromCh(ctx context.Context, t *trace.Table) (s // for the next waiter – session could be lost for a long time. c.internalPoolPutWaitCh(ch) } + return s, nil case <-createSessionTimeoutCh: c.mu.WithLock(func() { c.waitQ.Remove(el) }) + return nil, nil //nolint:nilnil case <-ctx.Done(): c.mu.WithLock(func() { c.waitQ.Remove(el) }) + return nil, xerrors.WithStackTrace(ctx.Err()) } } @@ -523,7 +543,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() { @@ -582,7 +602,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) }() @@ -627,17 +649,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) @@ -657,22 +678,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) @@ -700,6 +717,7 @@ func (c *Client) DoTx(ctx context.Context, op table.TxOperation, opts ...table.O } }() } + return op(xcontext.MarkRetryCall(ctx), tx) }() @@ -779,6 +797,7 @@ func (c *Client) internalPoolGetWaitCh() *chan *session { //nolint:gocritic if !ok { panic(fmt.Sprintf("%T is not a chan of sessions", ch)) } + return s } @@ -801,6 +820,7 @@ func (c *Client) internalPoolPeekFirstIdle() (s *session, touched time.Time) { if !has || el != info.idle { panic("inconsistent session client index") } + return s, info.touched } @@ -814,6 +834,7 @@ func (c *Client) internalPoolRemoveFirstIdle() *session { info := c.internalPoolRemoveIdle(s) c.index[s] = info } + return s } @@ -848,6 +869,7 @@ func (c *Client) internalPoolNotify(s *session) (notified bool) { close(*ch) } } + return false } @@ -869,6 +891,7 @@ func (c *Client) internalPoolRemoveIdle(s *session) sessionInfo { c.idle.Remove(info.idle) info.idle = nil c.index[s] = info + return info } diff --git a/internal/table/client_test.go b/internal/table/client_test.go index 90937d41c..82b7eb6af 100644 --- a/internal/table/client_test.go +++ b/internal/table/client_test.go @@ -8,6 +8,7 @@ import ( "path" "runtime" "sync" + "sync/atomic" "testing" "time" @@ -18,8 +19,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" + "github.com/ydb-platform/ydb-go-sdk/v3/internal/closer" "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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/xrand" @@ -122,6 +123,7 @@ func TestSessionPoolCloseWhenWaiting(t *testing.T) { &trace.Table{ OnPoolWait: func(trace.TablePoolWaitStartInfo) func(trace.TablePoolWaitDoneInfo) { wait <- struct{}{} + return nil }, }, @@ -139,6 +141,7 @@ func TestSessionPoolCloseWhenWaiting(t *testing.T) { withTrace(&trace.Table{ OnPoolGet: func(trace.TablePoolGetStartInfo) func(trace.TablePoolGetDoneInfo) { get <- struct{}{} + return nil }, }), @@ -346,10 +349,12 @@ func TestSessionPoolDeleteReleaseWait(t *testing.T) { &trace.Table{ OnPoolGet: func(trace.TablePoolGetStartInfo) func(trace.TablePoolGetDoneInfo) { get <- struct{}{} + return nil }, OnPoolWait: func(trace.TablePoolWaitStartInfo) func(trace.TablePoolWaitDoneInfo) { wait <- struct{}{} + return nil }, }, @@ -403,7 +408,7 @@ func TestSessionPoolRacyGet(t *testing.T) { session *session } create := make(chan createReq) - p, err := newClient( + p := newClient( context.Background(), nil, (&StubBuilder{ @@ -415,6 +420,7 @@ func TestSessionPoolRacyGet(t *testing.T) { } create <- req <-req.release + return req.session, nil }, }).createSession, @@ -423,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() { @@ -436,10 +442,12 @@ func TestSessionPoolRacyGet(t *testing.T) { s, e := p.Get(context.Background()) if e != nil { err = e + return } if s != expSession { err = fmt.Errorf("unexpected session: %v; want %v", s, expSession) + return } mustPutSession(t, p, s) @@ -553,10 +561,12 @@ func TestSessionPoolSizeLimitOverflow(t *testing.T) { withTrace(&trace.Table{ OnPoolGet: func(trace.TablePoolGetStartInfo) func(trace.TablePoolGetDoneInfo) { get <- struct{}{} + return nil }, OnPoolWait: func(trace.TablePoolWaitStartInfo) func(trace.TablePoolWaitDoneInfo) { wait <- struct{}{} + return nil }, }), @@ -628,12 +638,14 @@ func TestSessionPoolGetPut(t *testing.T) { testutil.InvokeHandlers{ testutil.TableCreateSession: func(interface{}) (proto.Message, error) { created++ + return &Ydb_Table.CreateSessionResult{ SessionId: testutil.SessionID(), }, nil }, testutil.TableDeleteSession: func(interface{}) (proto.Message, error) { deleted++ + return nil, nil }, }, @@ -666,7 +678,7 @@ func TestSessionPoolCloseIdleSessions(t *testing.T) { xtest.TestManyTimes(t, func(t testing.TB) { var ( idleThreshold = 4 * time.Second - closedCount xatomic.Int64 + closedCount atomic.Int64 fakeClock = clockwork.NewFakeClock() ) p := newClientWithStubBuilder( @@ -677,6 +689,7 @@ func TestSessionPoolCloseIdleSessions(t *testing.T) { testutil.TableDeleteSession: okHandler, testutil.TableCreateSession: func(interface{}) (proto.Message, error) { closedCount.Add(1) + return &Ydb_Table.CreateSessionResult{ SessionId: testutil.SessionID(), }, nil @@ -757,6 +770,7 @@ func mustGetSession(t testing.TB, p *Client) *session { t.Helper() t.Fatalf("%s: %v", caller(), err) } + return s } @@ -769,7 +783,7 @@ func mustPutSession(t testing.TB, p *Client, s *session) { } } -func mustClose(t testing.TB, p *Client) { +func mustClose(t testing.TB, p closer.Closer) { wg := sync.WaitGroup{} defer wg.Wait() if err := p.Close(context.Background()); err != nil { @@ -780,6 +794,7 @@ func mustClose(t testing.TB, p *Client) { func caller() string { _, file, line, _ := runtime.Caller(2) + return fmt.Sprintf("%s:%d", path.Base(file), line) } @@ -836,6 +851,7 @@ func simpleSession(t *testing.T) *session { if err != nil { t.Fatalf("newSession unexpected error: %v", err) } + return s } @@ -856,7 +872,7 @@ func newClientWithStubBuilder( stubLimit int, options ...config.Option, ) *Client { - c, err := newClient( + c := newClient( context.Background(), balancer, (&StubBuilder{ @@ -866,7 +882,7 @@ func newClientWithStubBuilder( }).createSession, config.New(options...), ) - require.NoError(t, err) + return c } @@ -912,6 +928,7 @@ func whenWantWaitCh(p *Client) <-chan struct{} { p.testHookGetWaitCh = prev close(ch) } + return ch } @@ -932,6 +949,7 @@ func TestDeadlockOnUpdateNodes(t *testing.T) { return nil, err } nodes = append(nodes, nodeID) + return &Ydb_Table.CreateSessionResult{ SessionId: sessionID, }, nil @@ -974,6 +992,7 @@ func TestDeadlockOnInternalPoolGCTick(t *testing.T) { return nil, err } nodes = append(nodes, nodeID) + return &Ydb_Table.CreateSessionResult{ SessionId: sessionID, }, nil diff --git a/internal/table/config/config.go b/internal/table/config/config.go index 6b8540f81..de94fb3e6 100644 --- a/internal/table/config/config.go +++ b/internal/table/config/config.go @@ -27,11 +27,12 @@ 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) } } + return c } diff --git a/internal/table/data_query.go b/internal/table/data_query.go index c5063a30e..f918b7192 100644 --- a/internal/table/data_query.go +++ b/internal/table/data_query.go @@ -36,6 +36,7 @@ func (q textDataQuery) YQL() string { func (q textDataQuery) toYDB(a *allocator.Allocator) *Ydb_Table.Query { query := a.TableQuery() query.Query = a.TableQueryYqlText(string(q)) + return query } @@ -54,6 +55,7 @@ func (q preparedDataQuery) YQL() string { func (q preparedDataQuery) toYDB(a *allocator.Allocator) *Ydb_Table.Query { query := a.TableQuery() query.Query = a.TableQueryID(q.id) + return query } diff --git a/internal/table/params.go b/internal/table/params.go deleted file mode 100644 index a4efeee38..000000000 --- a/internal/table/params.go +++ /dev/null @@ -1,40 +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/retry.go b/internal/table/retry.go index d04408094..d6577d7ad 100644 --- a/internal/table/retry.go +++ b/internal/table/retry.go @@ -46,6 +46,7 @@ func do( } }() } + return op(xcontext.MarkRetryCall(ctx), s) }() @@ -80,6 +81,7 @@ func retryBackoff( if err = op(ctx, s); err != nil { s.checkError(err) + return xerrors.WithStackTrace(err) } @@ -107,5 +109,6 @@ func (c *Client) retryOptions(opts ...table.Option) *table.Options { if options.Trace == nil { options.Trace = &trace.Table{} } + return options } diff --git a/internal/table/retry_test.go b/internal/table/retry_test.go index 43a1c865e..008633786 100644 --- a/internal/table/retry_test.go +++ b/internal/table/retry_test.go @@ -51,6 +51,7 @@ func TestRetryerBackoffRetryCancelation(t *testing.T) { testutil.BackoffFunc(func(n int) <-chan time.Time { ch := make(chan time.Time) backoff <- ch + return ch }), ), @@ -58,6 +59,7 @@ func TestRetryerBackoffRetryCancelation(t *testing.T) { testutil.BackoffFunc(func(n int) <-chan time.Time { ch := make(chan time.Time) backoff <- ch + return ch }), ), @@ -84,12 +86,14 @@ func TestRetryerBadSession(t *testing.T) { s.onClose = append(s.onClose, func(s *session) { closed[s] = true }) + return s, nil }, OnPut: func(ctx context.Context, s *session) error { if s.isClosing() { return s.Close(ctx) } + return nil }, } @@ -106,6 +110,7 @@ func TestRetryerBadSession(t *testing.T) { if i > maxRetryes { cancel() } + return xerrors.Operation( xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION), ) @@ -136,12 +141,14 @@ func TestRetryerSessionClosing(t *testing.T) { s.onClose = append(s.onClose, func(s *session) { closed[s] = true }) + return s, nil }, OnPut: func(ctx context.Context, s *session) error { if s.isClosing() { return s.Close(ctx) } + return nil }, } @@ -154,6 +161,7 @@ func TestRetryerSessionClosing(t *testing.T) { func(ctx context.Context, s table.Session) error { sessions = append(sessions, s) s.(*session).SetStatus(table.SessionClosing) + return nil }, nil, @@ -444,6 +452,7 @@ func TestRetryWithCustomErrors(t *testing.T) { if i < limit { return test.error } + return nil }, nil, @@ -486,6 +495,7 @@ func (f SessionProviderFunc) Get(ctx context.Context) (*session, error) { if f.OnGet == nil { return nil, xerrors.WithStackTrace(errNoSession) } + return f.OnGet(ctx) } @@ -493,6 +503,7 @@ func (f SessionProviderFunc) Put(ctx context.Context, s *session) error { if f.OnPut == nil { return xerrors.WithStackTrace(testutil.ErrNotImplemented) } + return f.OnPut(ctx, s) } @@ -512,6 +523,7 @@ func (s *singleSession) Get(context.Context) (*session, error) { return nil, xerrors.WithStackTrace(errNoSession) } s.empty = true + return s.s, nil } @@ -523,6 +535,7 @@ func (s *singleSession) Put(_ context.Context, x *session) error { return xerrors.WithStackTrace(errSessionOverflow) } s.empty = false + return nil } diff --git a/internal/table/scanner/result.go b/internal/table/scanner/result.go index e620e19bd..1cbc88eb6 100644 --- a/internal/table/scanner/result.go +++ b/internal/table/scanner/result.go @@ -4,11 +4,11 @@ import ( "context" "errors" "io" + "sync/atomic" "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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/table/result" @@ -18,13 +18,13 @@ import ( var errAlreadyClosed = xerrors.Wrap(errors.New("result closed early")) type baseResult struct { - scanner + valueScanner - nextResultSetCounter xatomic.Uint64 + nextResultSetCounter atomic.Uint64 statsMtx xsync.RWMutex stats *Ydb_TableStats.QueryStats - closed xatomic.Bool + closed atomic.Bool } type streamResult struct { @@ -36,10 +36,11 @@ 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) } + return nil } @@ -52,10 +53,11 @@ 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) } + return nil } @@ -64,6 +66,7 @@ func (r *unaryResult) Close() error { if r.closed.CompareAndSwap(false, true) { return nil } + return xerrors.WithStackTrace(errAlreadyClosed) } @@ -93,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 } } @@ -113,14 +116,15 @@ 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 { return nil, xerrors.WithStackTrace(err) } + return r, nil } @@ -131,11 +135,12 @@ 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) } } + return r } @@ -155,6 +160,7 @@ func (r *unaryResult) NextResultSetErr(ctx context.Context, columns ...string) ( } r.Reset(r.sets[r.nextSet], columns...) r.nextSet++ + return ctx.Err() } @@ -166,6 +172,7 @@ func (r *streamResult) nextResultSetErr(ctx context.Context, columns ...string) // skipping second recv because first call of recv is from New Stream(), second call is from user if r.nextResultSetCounter.Add(1) == 2 { r.setColumnIndexes(columns) + return ctx.Err() } s, stats, err := r.recv(ctx) @@ -174,6 +181,7 @@ func (r *streamResult) nextResultSetErr(ctx context.Context, columns ...string) if xerrors.Is(err, io.EOF) { return err } + return r.errorf(1, "streamResult.NextResultSetErr(): %w", err) } r.Reset(s, columns...) @@ -182,6 +190,7 @@ func (r *streamResult) nextResultSetErr(ctx context.Context, columns ...string) r.stats = stats }) } + return ctx.Err() } @@ -196,8 +205,10 @@ func (r *streamResult) NextResultSetErr(ctx context.Context, columns ...string) if xerrors.Is(err, io.EOF) { return io.EOF } + return xerrors.WithStackTrace(err) } + return nil } @@ -229,6 +240,7 @@ func (r *streamResult) Close() (err error) { if r.closed.CompareAndSwap(false, true) { return r.close(r.Err()) } + return xerrors.WithStackTrace(errAlreadyClosed) } @@ -252,5 +264,6 @@ func (r *unaryResult) HasNextResultSet() bool { if r.inactive() || r.nextSet >= len(r.sets) { return false } + return true } diff --git a/internal/table/scanner/result_test.go b/internal/table/scanner/result_test.go index e7eb25e36..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) @@ -201,6 +201,7 @@ func NewResultSet(a *allocator.Allocator, opts ...ResultSetOption) *Ydb.ResultSe opt(&d, a) } } + return (*Ydb.ResultSet)(&d) } @@ -218,6 +219,7 @@ func TestNewStreamWithRecvFirstResultSet(t *testing.T) { ctx: func() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() + return ctx }(), err: context.Canceled, @@ -226,6 +228,7 @@ func TestNewStreamWithRecvFirstResultSet(t *testing.T) { ctx: func() context.Context { ctx, cancel := context.WithTimeout(context.Background(), 0) cancel() + return ctx }(), err: context.DeadlineExceeded, @@ -234,6 +237,7 @@ func TestNewStreamWithRecvFirstResultSet(t *testing.T) { ctx: func() context.Context { ctx, cancel := context.WithTimeout(context.Background(), time.Hour) cancel() + return ctx }(), err: context.Canceled, @@ -246,6 +250,7 @@ func TestNewStreamWithRecvFirstResultSet(t *testing.T) { if tt.recvCounter > 1000 { return nil, nil, io.EOF } + return &Ydb.ResultSet{}, nil, ctx.Err() }, func(err error) error { diff --git a/internal/table/scanner/scan_raw.go b/internal/table/scanner/scan_raw.go index b9e7732d9..7c0782f3c 100644 --- a/internal/table/scanner/scan_raw.go +++ b/internal/table/scanner/scan_raw.go @@ -10,18 +10,20 @@ 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) { s.unwrap() + return s.bytes() } @@ -30,12 +32,13 @@ 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 { var buf bytes.Buffer _, _ = s.WritePathTo(&buf) + return buf.String() } @@ -61,6 +64,7 @@ func (s *rawConverter) WritePathTo(w io.Writer) (n int64, err error) { } n += int64(m) } + return n, nil } @@ -73,6 +77,7 @@ func (s *rawConverter) Bool() (v bool) { return } s.unwrap() + return s.bool() } @@ -81,6 +86,7 @@ func (s *rawConverter) Int8() (v int8) { return } s.unwrap() + return s.int8() } @@ -89,6 +95,7 @@ func (s *rawConverter) Uint8() (v uint8) { return } s.unwrap() + return s.uint8() } @@ -97,6 +104,7 @@ func (s *rawConverter) Int16() (v int16) { return } s.unwrap() + return s.int16() } @@ -105,6 +113,7 @@ func (s *rawConverter) Uint16() (v uint16) { return } s.unwrap() + return s.uint16() } @@ -113,6 +122,7 @@ func (s *rawConverter) Int32() (v int32) { return } s.unwrap() + return s.int32() } @@ -121,6 +131,7 @@ func (s *rawConverter) Uint32() (v uint32) { return } s.unwrap() + return s.uint32() } @@ -129,6 +140,7 @@ func (s *rawConverter) Int64() (v int64) { return } s.unwrap() + return s.int64() } @@ -137,6 +149,7 @@ func (s *rawConverter) Uint64() (v uint64) { return } s.unwrap() + return s.uint64() } @@ -145,6 +158,7 @@ func (s *rawConverter) Float() (v float32) { return } s.unwrap() + return s.float() } @@ -153,26 +167,31 @@ func (s *rawConverter) Double() (v float64) { return } s.unwrap() + return s.double() } func (s *rawConverter) Date() (v time.Time) { s.unwrap() + return value.DateToTime(s.uint32()) } func (s *rawConverter) Datetime() (v time.Time) { s.unwrap() + return value.DatetimeToTime(s.uint32()) } func (s *rawConverter) Timestamp() (v time.Time) { s.unwrap() + return value.TimestampToTime(s.uint64()) } func (s *rawConverter) Interval() (v time.Duration) { s.unwrap() + return value.IntervalToDuration(s.int64()) } @@ -185,6 +204,7 @@ func (s *rawConverter) TzDate() (v time.Time) { if err != nil { _ = s.errorf(0, "rawConverter.TzDate(): %w", err) } + return src } @@ -197,6 +217,7 @@ func (s *rawConverter) TzDatetime() (v time.Time) { if err != nil { _ = s.errorf(0, "rawConverter.TzDatetime(): %w", err) } + return src } @@ -209,6 +230,7 @@ func (s *rawConverter) TzTimestamp() (v time.Time) { if err != nil { _ = s.errorf(0, "rawConverter.TzTimestamp(): %w", err) } + return src } @@ -217,21 +239,25 @@ func (s *rawConverter) UTF8() (v string) { return } s.unwrap() + return s.text() } func (s *rawConverter) YSON() (v []byte) { s.unwrap() + return s.bytes() } func (s *rawConverter) JSON() (v []byte) { s.unwrap() + return xstring.ToBytes(s.text()) } func (s *rawConverter) JSONDocument() (v []byte) { s.unwrap() + return xstring.ToBytes(s.text()) } @@ -240,6 +266,7 @@ func (s *rawConverter) UUID() (v [16]byte) { return } s.unwrap() + return s.uint128() } @@ -248,6 +275,7 @@ func (s *rawConverter) DyNumber() (v string) { return } s.unwrap() + return s.text() } @@ -255,12 +283,13 @@ 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 } s.unwrap() + return s.value() } @@ -279,6 +308,7 @@ func (s *rawConverter) IsNull() bool { if s.Err() != nil { return false } + return s.isNull() } @@ -286,6 +316,7 @@ func (s *rawConverter) IsOptional() bool { if s.Err() != nil { return false } + return s.isCurrentTypeOptional() } @@ -299,6 +330,7 @@ func (s *rawConverter) ListIn() (size int) { if s.assertTypeList(x.t) != nil { return s.itemsIn() } + return 0 } @@ -307,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], }) } } @@ -337,6 +369,7 @@ func (s *rawConverter) TupleIn() (size int) { if s.assertTypeTuple(x.t) != nil { return s.itemsIn() } + return 0 } @@ -345,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], }) } } @@ -375,6 +408,7 @@ func (s *rawConverter) StructIn() (size int) { if s.assertTypeStruct(x.t) != nil { return s.itemsIn() } + return 0 } @@ -383,19 +417,20 @@ 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], }) } + return } @@ -417,6 +452,7 @@ func (s *rawConverter) DictIn() (size int) { if s.assertTypeDict(x.t) != nil { return s.pairsIn() } + return 0 } @@ -425,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(), }) } } @@ -442,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(), }) } } @@ -485,6 +521,7 @@ func (s *rawConverter) Variant() (name string, index uint32) { t: typ, v: v, }) + return name, index } @@ -498,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, }) } @@ -517,22 +554,24 @@ func (s *rawConverter) Decimal(t types.Type) (v [16]byte) { if !s.assertCurrentTypeDecimal(t) { return } + 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(), } } @@ -540,37 +579,44 @@ func (s *rawConverter) IsDecimal() bool { if s.Err() != nil { return false } + return s.isCurrentTypeDecimal() } func isEqualDecimal(d *Ydb.DecimalType, t types.Type) bool { - w := t.(*value.DecimalType) - return d.Precision == w.Precision && d.Scale == w.Scale + w := t.(*types.Decimal) + + 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] - return m.Name, m.Type + m := x.StructItems.GetMembers()[i] + + return m.GetName(), m.GetType() default: panic("unexpected variant items types") @@ -583,7 +629,8 @@ 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 } @@ -593,7 +640,8 @@ func (s *rawConverter) itemsIn() int { return -1 } s.stack.enter() - return len(x.v.Items) + + return len(x.v.GetItems()) } func (s *rawConverter) itemsOut() { @@ -610,7 +658,8 @@ func (s *rawConverter) pairsIn() int { return -1 } s.stack.enter() - return len(x.v.Pairs) + + return len(x.v.GetPairs()) } func (s *rawConverter) pairsOut() { @@ -624,16 +673,19 @@ func (s *rawConverter) pairsBoundsCheck(xs []*Ydb.ValuePair, i int) bool { func (s *rawConverter) boundsCheck(n, i int) bool { if i < 0 || n <= i { s.boundsError(n, i) + return false } + 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) } + return } @@ -655,13 +707,14 @@ func (s *rawConverter) assertCurrentTypeNullable() bool { c.t, p.t, ) + return false } 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", @@ -670,8 +723,10 @@ func (s *rawConverter) assertCurrentTypeIs(t types.Type) bool { act, t, ) + return false } + return true } @@ -682,56 +737,64 @@ func (s *rawConverter) assertCurrentTypeDecimal(t types.Type) bool { } if !isEqualDecimal(d.DecimalType, t) { s.decimalTypeError(t) + return false } + return true } 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) } + return } 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) } + return } 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) } + return } 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) } + return } 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) } + return } 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) } + return } @@ -756,8 +819,9 @@ 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") + return s } diff --git a/internal/table/scanner/scanner.go b/internal/table/scanner/scanner.go index 7334dc4d5..f541a1235 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,56 +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() @@ -96,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( @@ -113,10 +118,11 @@ func (s *scanner) preScanChecks(lenValues int) (err error) { if s.nextItem != 0 { panic("scan row failed: double scan per row") } + 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 } @@ -140,10 +146,11 @@ func (s *scanner) ScanWithDefaults(values ...indexed.Required) (err error) { } } s.nextItem += len(values) + 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 } @@ -167,10 +174,11 @@ func (s *scanner) Scan(values ...indexed.RequiredOrOptional) (err error) { } } s.nextItem += len(values) + 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 } @@ -192,32 +200,36 @@ 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) + return s.Err() } // 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 { @@ -230,13 +242,15 @@ func (s *scanner) Err() error { if s.markTruncatedAsRetryable { err = xerrors.Retryable(err) } + return xerrors.WithStackTrace(err) } + return nil } // 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 @@ -245,18 +259,19 @@ 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) + 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) @@ -264,65 +279,73 @@ func (s *scanner) writePathTo(w io.Writer) (n int64, err error) { return n, xerrors.WithStackTrace(err) } n += int64(m) + 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() } + return s.notFoundColumnName(name) } -func (s *scanner) setColumnIndexes(columns []string) { +func (s *valueScanner) setColumnIndexes(columns []string) { if columns == nil { s.columnIndexes = nil + return } 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 + break } } if !found { _ = s.noColumnError(col) + return } } @@ -347,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 @@ -362,97 +385,104 @@ 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") + return nil } } -// 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 } @@ -462,190 +492,224 @@ 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) + return } + 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) } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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) + return } + 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") + 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()) @@ -656,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() @@ -690,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() @@ -704,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() @@ -715,6 +779,7 @@ func (s *scanner) trySetByteArray(v interface{}, optional, def bool) bool { } if s.isNull() { rv.Set(reflect.Zero(rv.Type())) + return true } if rv.IsZero() { @@ -731,6 +796,7 @@ func (s *scanner) trySetByteArray(v interface{}, optional, def bool) bool { } if def { rv.Set(reflect.Zero(rv.Type())) + return true } var dst []byte @@ -739,11 +805,12 @@ func (s *scanner) trySetByteArray(v interface{}, optional, def bool) bool { return false } reflect.Copy(rv, reflect.ValueOf(dst)) + return true } //nolint:gocyclo -func (s *scanner) scanRequired(v interface{}) { +func (s *valueScanner) scanRequired(v interface{}) { switch v := v.(type) { case *bool: *v = s.bool() @@ -783,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) @@ -800,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()) @@ -819,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) @@ -827,6 +894,7 @@ func (s *scanner) scanOptional(v interface{}, defaultValueForOptional bool) { s.unwrap() s.scanRequired(v) } + return } switch v := v.(type) { @@ -969,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) @@ -992,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 { @@ -1024,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 @@ -1060,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) @@ -1093,17 +1161,18 @@ 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 { return s.err } s.err = xerrors.WithStackTrace(fmt.Errorf(f, args...), xerrors.WithSkipDepth(depth+1)) + return s.err } -func (s *scanner) typeError(act, exp interface{}) { +func (s *valueScanner) typeError(act, exp interface{}) { _ = s.errorf( 2, "unexpected types during scan at %q %s: %s; want %s", @@ -1114,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, @@ -1126,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, "not found %d column", @@ -1134,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, "not found column '%s'", @@ -1142,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, "no column %q", @@ -1150,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, "overflow error: %d overflows capacity of %t", @@ -1163,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 } @@ -1182,6 +1251,7 @@ func (s *scanStack) size() int { if !s.scanItem.isEmpty() { s.set(s.scanItem) } + return s.p + 1 } @@ -1222,6 +1292,7 @@ func (s *scanStack) parent() item { if s.p == 0 { return emptyItem } + return s.v[s.p-1] } @@ -1232,20 +1303,23 @@ func (s *scanStack) current() item { if s.v == nil { return emptyItem } + return s.v[s.p] } func (s *scanStack) currentValue() interface{} { if v := s.current().v; v != nil { - return v.Value + return v.GetValue() } + return nil } func (s *scanStack) currentType() interface{} { if t := s.current().t; t != nil { - return t.Type + return t.GetType() } + return nil } @@ -1253,6 +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 c3d7c73a2..f47fffbac 100644 --- a/internal/table/scanner/scanner_data_test.go +++ b/internal/table/scanner/scanner_data_test.go @@ -29,6 +29,7 @@ func (s *intIncScanner) Scan(src interface{}) error { return fmt.Errorf("wrong type: %T, exp: int64", src) } *s = intIncScanner(v + 10) + return nil } @@ -40,6 +41,7 @@ func (s *dateScanner) Scan(src interface{}) error { return fmt.Errorf("wrong type: %T, exp: time.Time", src) } *s = dateScanner(v) + return nil } @@ -206,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", @@ -468,8 +470,8 @@ var scannerData = []struct { }, } -func initScanner() *scanner { - res := scanner{ +func initScanner() *valueScanner { + res := valueScanner{ set: &Ydb.ResultSet{ Columns: nil, Rows: nil, @@ -485,10 +487,11 @@ func initScanner() *scanner { columnIndexes: nil, err: nil, } + return &res } -func PrepareScannerPerformanceTest(count int) *scanner { +func PrepareScannerPerformanceTest(count int) *valueScanner { res := initScanner() res.set.Columns = []*Ydb.Column{{ Name: "series_id", @@ -526,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), @@ -543,5 +546,6 @@ func PrepareScannerPerformanceTest(count int) *scanner { }) } res.converter = &rawConverter{res} + return res } diff --git a/internal/table/scanner/scanner_test.go b/internal/table/scanner/scanner_test.go index c0dcb4c5f..44f09ff45 100644 --- a/internal/table/scanner/scanner_test.go +++ b/internal/table/scanner/scanner_test.go @@ -31,8 +31,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_INT8: v := int8(rv) @@ -43,8 +45,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_UINT8: if c.nilValue { @@ -53,9 +57,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv uint8 + return ydbval, &dv } var dv *uint8 + return ydbval, &dv } v := uint8(rv) @@ -66,8 +72,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_INT16: v := int16(rv) @@ -78,8 +86,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_UINT16: v := uint16(rv) @@ -90,8 +100,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_INT32: if c.nilValue { @@ -100,9 +112,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv int32 + return ydbval, &dv } var dv *int32 + return ydbval, &dv } v := int32(rv) @@ -113,8 +127,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_UINT32: v := uint32(rv) @@ -125,8 +141,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_INT64: v := rv @@ -137,16 +155,20 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.ydbvalue { vp := types.Int64Value(v) + return ydbval, &vp } if c.scanner { s := intIncScanner(v + 10) + return ydbval, &s } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_UINT64: v := uint64(rv) @@ -157,8 +179,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_FLOAT: v := float32(rv) @@ -169,12 +193,15 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.ydbvalue { vp := types.FloatValue(v) + return ydbval, &vp } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_DOUBLE: v := float64(rv) @@ -185,8 +212,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_DATE: v := uint32(rv) @@ -198,12 +227,15 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := value.DateToTime(v) if c.scanner { s := dateScanner(src) + return ydbval, &s } if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_DATETIME: v := uint32(rv) @@ -215,8 +247,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := value.DatetimeToTime(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_TIMESTAMP: v := uint64(rv) @@ -228,8 +262,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := value.TimestampToTime(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_INTERVAL: if c.nilValue { @@ -238,9 +274,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv time.Duration + return ydbval, &dv } var dv *time.Duration + return ydbval, &dv } rv %= time.Now().Unix() @@ -253,8 +291,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := value.IntervalToDuration(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_TZ_DATE: v := time.Now().Format(value.LayoutDate) + ",Europe/Berlin" @@ -266,8 +306,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src, _ := value.TzDateToTime(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_TZ_DATETIME: if c.nilValue { @@ -276,9 +318,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv time.Time + return ydbval, &dv } var dv *time.Time + return ydbval, &dv } rv %= time.Now().Unix() @@ -291,8 +335,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src, _ := value.TzDatetimeToTime(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_TZ_TIMESTAMP: rv %= time.Now().Unix() @@ -305,8 +351,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src, _ := value.TzTimestampToTime(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_STRING: if c.nilValue { @@ -315,9 +363,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv []byte + return ydbval, &dv } var dv *[]byte + return ydbval, &dv } v := make([]byte, 16) @@ -331,8 +381,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := v if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_UTF8: v := strconv.FormatUint(uint64(rv), 10) @@ -343,8 +395,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_YSON: if c.nilValue { @@ -353,9 +407,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv []byte + return ydbval, &dv } var dv *[]byte + return ydbval, &dv } v := strconv.FormatUint(uint64(rv), 10) @@ -367,8 +423,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := []byte(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_JSON: v := strconv.FormatUint(uint64(rv), 10) @@ -379,13 +437,16 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.ydbvalue { vp := types.JSONValue(v) + return ydbval, &vp } src := []byte(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_UUID: if c.nilValue { @@ -394,9 +455,11 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.testDefault { var dv [16]byte + return ydbval, &dv } var dv *[16]byte + return ydbval, &dv } v := [16]byte{} @@ -410,8 +473,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v case Ydb.Type_JSON_DOCUMENT: v := strconv.FormatUint(uint64(rv), 10) @@ -423,8 +488,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) src := []byte(v) if c.optional && !c.testDefault { vp := &src + return ydbval, &vp } + return ydbval, &src case Ydb.Type_DYNUMBER: v := strconv.FormatUint(uint64(rv), 10) @@ -435,8 +502,10 @@ func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) } if c.optional && !c.testDefault { vp := &v + return ydbval, &vp } + return ydbval, &v default: panic("ydb: unexpected types") @@ -461,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, @@ -474,16 +543,17 @@ 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 } + return result, testValues } @@ -499,6 +569,7 @@ func TestScanSqlTypes(t *testing.T) { for _, v := range test.values { values = append(values, v) } + return values }()...); err != nil { t.Fatalf("test: %s; error: %s", test.name, err) @@ -527,6 +598,7 @@ func TestScanNamed(t *testing.T) { if columns == nil { return defaultValue } + return columns[i] } for _, test := range scannerData { @@ -602,6 +674,7 @@ type jsonUnmarshaller struct { func (json *jsonUnmarshaller) UnmarshalJSON(bytes []byte) error { json.bytes = bytes + return nil } diff --git a/internal/table/scanner/stats.go b/internal/table/scanner/stats.go index cfe3295aa..d9772e34c 100644 --- a/internal/table/scanner/stats.go +++ b/internal/table/scanner/stats.go @@ -20,13 +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()), } } @@ -39,31 +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 } @@ -87,11 +89,12 @@ func (q *queryPhase) NextTableAccess() (t *stats.TableAccess, ok bool) { } x := q.tables[q.pos] 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 } @@ -115,8 +118,9 @@ func initOperationStats(x *Ydb_TableStats.OperationStats) stats.OperationStats { if x == nil { return 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 045ebc00d..3c9fe585c 100644 --- a/internal/table/session.go +++ b/internal/table/session.go @@ -6,6 +6,7 @@ import ( "net/url" "strconv" "sync" + "sync/atomic" "time" "github.com/ydb-platform/ydb-go-genproto/Ydb_Table_V1" @@ -21,18 +22,18 @@ 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/xatomic" "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/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 xatomic.Uint32 - lastUsage xatomic.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 { @@ -70,6 +69,7 @@ func nodeID(sessionID string) (uint32, error) { if err != nil { return 0, err } + return uint32(id), err } @@ -85,6 +85,7 @@ func (s *session) NodeID() uint32 { return 0 } s.nodeID.Store(id) + return id } @@ -94,6 +95,7 @@ func (s *session) Status() table.SessionStatus { } s.statusMtx.RLock() defer s.statusMtx.RUnlock() + return s.status } @@ -114,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) }() @@ -166,6 +170,7 @@ func (s *session) ID() string { if s == nil { return "" } + return s.id } @@ -176,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() { @@ -228,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, ) ) @@ -256,12 +261,13 @@ 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: s.SetStatus(table.SessionBusy) } + return nil } @@ -294,6 +300,7 @@ func (s *session) CreateTable( if err != nil { return xerrors.WithStackTrace(err) } + return nil } @@ -335,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(), } } @@ -347,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 @@ -370,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()), @@ -408,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: @@ -426,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) } @@ -472,6 +479,7 @@ func (s *session) DropTable( } } _, err = s.tableService.DropTable(ctx, &request) + return xerrors.WithStackTrace(err) } @@ -479,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) } } @@ -510,6 +518,7 @@ func (s *session) AlterTable( } } _, err = s.tableService.AlterTable(ctx, &request) + return xerrors.WithStackTrace(err) } @@ -539,6 +548,7 @@ func (s *session) CopyTable( if err != nil { return xerrors.WithStackTrace(err) } + return nil } @@ -568,13 +578,14 @@ 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) if err != nil { return xerrors.WithStackTrace(err) } + return nil } @@ -587,6 +598,7 @@ func (s *session) CopyTables( if err != nil { return xerrors.WithStackTrace(err) } + return nil } @@ -603,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, ) ) @@ -640,7 +652,7 @@ func (s *session) Explain( Explanation: table.Explanation{ Plan: result.GetQueryPlan(), }, - AST: result.QueryAst, + AST: result.GetQueryAst(), }, nil } @@ -652,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, ) ) @@ -688,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 @@ -699,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, @@ -717,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(), @@ -735,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() { @@ -764,12 +776,13 @@ 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) tx.control = table.TxControl(table.WithTxID(tx.id)) } + return tx, scanner.NewUnary( res.GetResultSets(), res.GetQueryStats(), @@ -825,6 +838,7 @@ func (s *session) ExecuteSchemeQuery( } } _, err = s.tableService.ExecuteSchemeQuery(ctx, &request) + return xerrors.WithStackTrace(err) } @@ -972,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{ @@ -985,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 { @@ -1001,6 +1013,7 @@ func (s *session) StreamReadTable( stream, err = s.tableService.StreamReadTable(ctx, &request) if err != nil { cancel() + return nil, xerrors.WithStackTrace(err) } @@ -1010,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()) @@ -1023,12 +1033,14 @@ func (s *session) StreamReadTable( if result == nil || err != nil { return nil, nil, xerrors.WithStackTrace(err) } + return result.GetResultSet(), nil, nil } }, func(err error) error { cancel() - onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err)) + onDone(xerrors.HideEOF(err)) + return err }, scanner.WithIgnoreTruncated(true), // stream read table always returns truncated flag on last result set @@ -1038,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 ( @@ -1067,9 +1079,7 @@ func (s *session) ReadRows( if response.GetStatus() != Ydb.StatusIds_SUCCESS { return nil, xerrors.WithStackTrace( - xerrors.Operation( - xerrors.FromOperation(response), - ), + xerrors.FromOperation(response), ) } @@ -1088,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 @@ -1109,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 { @@ -1125,6 +1133,7 @@ func (s *session) StreamExecuteScanQuery( stream, err = s.tableService.StreamExecuteScanQuery(ctx, &request, callOptions...) if err != nil { cancel() + return nil, xerrors.WithStackTrace(err) } @@ -1134,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()) @@ -1147,12 +1153,14 @@ func (s *session) StreamExecuteScanQuery( if result == nil || err != nil { return nil, nil, xerrors.WithStackTrace(err) } + return result.GetResultSet(), result.GetQueryStats(), nil } }, func(err error) error { cancel() - onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err)) + onDone(xerrors.HideEOF(err)) + return err }, scanner.WithIgnoreTruncated(s.config.IgnoreTruncated()), @@ -1161,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 ( @@ -1169,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, ) ) @@ -1179,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, @@ -1211,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, ) ) @@ -1246,5 +1256,6 @@ func (s *session) BeginTransaction( control: table.TxControl(table.WithTxID(result.GetTxMeta().GetId())), } tx.state.Store(txStateInitialized) + return tx, nil } diff --git a/internal/table/session_test.go b/internal/table/session_test.go index d841c5fd4..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" ) @@ -106,6 +106,7 @@ func TestSessionDescribeTable(t *testing.T) { testutil.TableDescribeTable: func(interface{}) (proto.Message, error) { r := &Ydb_Table.DescribeTableResult{} proto.Merge(r, result) + return r, e }, }, @@ -132,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, }, }, @@ -181,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", }, }, @@ -338,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) { @@ -381,7 +382,6 @@ func TestSessionOperationModeOnExecuteDataQuery(t *testing.T) { }, ), ), config.New()) - require.NoError(t, err) ctx, cancel := xcontext.WithTimeout( context.Background(), time.Second, @@ -396,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) { @@ -466,27 +466,26 @@ func TestCreateTableRegression(t *testing.T) { //nolint:revive return nil, fmt.Errorf("proto's not equal: \n\nact: %v\n\nexp: %s\n\n", act, exp) } + return &Ydb_Table.CreateTableResponse{}, nil }, }, ), ), 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"), ) @@ -496,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) { @@ -565,8 +564,6 @@ func TestDescribeTableRegression(t *testing.T) { ), ), config.New()) - require.NoError(t, err) - ctx, cancel := xcontext.WithTimeout( context.Background(), time.Second, @@ -575,8 +572,9 @@ 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 }) @@ -587,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{ @@ -637,6 +635,7 @@ func (mock *copyTablesMock) CopyTables( if in.String() == mock.String() { return &Ydb_Table.CopyTablesResponse{}, nil } + return nil, fmt.Errorf("%w: %s, exp: %s", errUnexpectedRequest, in, mock.String()) } diff --git a/internal/table/statement.go b/internal/table/statement.go index e59f07b5a..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() { @@ -84,6 +85,7 @@ func (s *statement) execute( if err != nil { return nil, nil, xerrors.WithStackTrace(err) } + return s.session.executeQueryResult(res, txControl, request.IgnoreTruncated) } diff --git a/internal/table/transaction.go b/internal/table/transaction.go index 180784c3a..a07576196 100644 --- a/internal/table/transaction.go +++ b/internal/table/transaction.go @@ -3,14 +3,15 @@ package table import ( "context" "fmt" + "sync/atomic" "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/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/xatomic" "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/options" @@ -24,7 +25,7 @@ var ( ) type txState struct { - rawVal xatomic.Uint32 + rawVal atomic.Uint32 } func (s *txState) Load() txStateEnum { @@ -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/transaction_test.go b/internal/table/transaction_test.go index c08d19e25..cc4e88489 100644 --- a/internal/table/transaction_test.go +++ b/internal/table/transaction_test.go @@ -31,6 +31,7 @@ func TestTxSkipRollbackForCommitted(t *testing.T) { t.Fatalf("cannot cast request '%T' to *Ydb_Table.BeginTransactionRequest", request) } begin++ + return &Ydb_Table.BeginTransactionResult{ TxMeta: &Ydb_Table.TransactionMeta{ Id: "", @@ -43,6 +44,7 @@ func TestTxSkipRollbackForCommitted(t *testing.T) { t.Fatalf("cannot cast request '%T' to *Ydb_Table.CommitTransactionRequest", request) } commit++ + return &Ydb_Table.CommitTransactionResult{}, nil }, testutil.TableRollbackTransaction: func(request interface{}) (proto.Message, error) { @@ -51,6 +53,7 @@ func TestTxSkipRollbackForCommitted(t *testing.T) { t.Fatalf("cannot cast request '%T' to *Ydb_Table.RollbackTransactionRequest", request) } rollback++ + return &Ydb_Table.RollbackTransactionResponse{ Operation: &Ydb_Operations.Operation{ Ready: true, diff --git a/internal/table/ttl.go b/internal/table/ttl.go index c734847d9..fd3ad945e 100644 --- a/internal/table/ttl.go +++ b/internal/table/ttl.go @@ -10,22 +10,23 @@ 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, } } + return nil } @@ -45,5 +46,6 @@ func timeToLiveUnit(unit Ydb_Table.ValueSinceUnixEpochModeSettings_Unit) *option default: panic("ydb: unknown Ydb unit for value since epoch") } + return &res } diff --git a/internal/topic/retriable_error.go b/internal/topic/retriable_error.go index 5d08b5087..23ef3c49c 100644 --- a/internal/topic/retriable_error.go +++ b/internal/topic/retriable_error.go @@ -7,12 +7,14 @@ import ( "time" "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" + "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/retry" ) const ( - DefaultStartTimeout = time.Minute + DefaultStartTimeout = time.Minute + connectionEstablishedTimeout = time.Minute ) type RetrySettings struct { @@ -44,6 +46,10 @@ var ( func CheckResetReconnectionCounters(lastTry, now time.Time, connectionTimeout time.Duration) bool { const resetAttemptEmpiricalCoefficient = 10 + if connectionTimeout == value.InfiniteDuration { + return now.Sub(lastTry) > connectionEstablishedTimeout + } + return now.Sub(lastTry) > connectionTimeout*resetAttemptEmpiricalCoefficient } @@ -87,9 +93,10 @@ func CheckRetryMode(err error, settings RetrySettings, retriesDuration time.Dura return nil, false } - if mode.BackoffType() == backoff.TypeFast { + switch mode.BackoffType() { + case backoff.TypeFast: return backoff.Fast, true + default: + return backoff.Slow, true } - - return backoff.Slow, true } diff --git a/internal/topic/retriable_error_test.go b/internal/topic/retriable_error_test.go index 79e8e7128..9de4b2c00 100644 --- a/internal/topic/retriable_error_test.go +++ b/internal/topic/retriable_error_test.go @@ -12,6 +12,7 @@ import ( grpcStatus "google.golang.org/grpc/status" "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" + "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" ) @@ -232,3 +233,45 @@ func TestCheckRetryMode(t *testing.T) { }) } } + +func TestCheckResetReconnectionCounters(t *testing.T) { + now := time.Now() + table := []struct { + name string + lastTry time.Time + connectionTimeout time.Duration + shouldReset bool + }{ + { + name: "RecentLastTryWithInfiniteConnectionTimeout", + lastTry: now.Add(-30 * time.Second), + connectionTimeout: value.InfiniteDuration, + shouldReset: false, + }, + { + name: "OldLastTryWithInfiniteConnectionTimeout", + lastTry: now.Add(-30 * time.Minute), + connectionTimeout: value.InfiniteDuration, + shouldReset: true, + }, + { + name: "LastTryLessThanConnectionTimeout", + lastTry: now.Add(-30 * time.Second), + connectionTimeout: time.Minute, + shouldReset: false, + }, + { + name: "LastTryGreaterThanConnectionTimeout", + lastTry: now.Add(-time.Hour), + connectionTimeout: time.Minute, + shouldReset: true, + }, + } + + for _, test := range table { + t.Run(test.name, func(t *testing.T) { + shouldReset := CheckResetReconnectionCounters(test.lastTry, now, test.connectionTimeout) + require.Equal(t, test.shouldReset, shouldReset) + }) + } +} diff --git a/internal/topic/topicclientinternal/client.go b/internal/topic/topicclientinternal/client.go index d774a30fb..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,18 +45,19 @@ 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) } } + return c } @@ -70,14 +71,15 @@ 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) } } call := func(ctx context.Context) error { _, alterErr := c.rawClient.AlterTopic(ctx, req) + return alterErr } @@ -101,14 +103,15 @@ 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) } } call := func(ctx context.Context) error { _, createErr := c.rawClient.CreateTopic(ctx, req) + return createErr } @@ -133,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) } } @@ -143,6 +146,7 @@ func (c *Client) Describe( call := func(ctx context.Context) (describeErr error) { rawRes, describeErr = c.rawClient.DescribeTopic(ctx, req) + return describeErr } @@ -162,6 +166,7 @@ func (c *Client) Describe( } res.FromRaw(&rawRes) + return res, nil } @@ -171,14 +176,15 @@ 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) } } call := func(ctx context.Context) error { _, removeErr := c.rawClient.DropTopic(ctx, req) + return removeErr } @@ -215,6 +221,7 @@ func (c *Client) StartReader( internalReader := topicreaderinternal.NewReader(connector, consumer, readSelectors, opts...) trace.TopicOnReaderStart(internalReader.Tracer(), internalReader.ID(), consumer) + return topicreader.NewReader(internalReader), nil } @@ -240,5 +247,6 @@ func (c *Client) StartWriter(topicPath string, opts ...topicoptions.WriterOption if err != nil { return nil, err } + return topicwriter.NewWriter(writer), nil } diff --git a/internal/topic/topicreaderinternal/batch.go b/internal/topic/topicreaderinternal/batch.go index 14283d0fe..e8e00ad01 100644 --- a/internal/topic/topicreaderinternal/batch.go +++ b/internal/topic/topicreaderinternal/batch.go @@ -143,6 +143,7 @@ func (m *PublicBatch) append(b *PublicBatch) (*PublicBatch, error) { res.Messages = append(res.Messages, b.Messages...) res.commitRange.commitOffsetEnd = b.commitRange.commitOffsetEnd + return res, nil } @@ -157,6 +158,7 @@ func (m *PublicBatch) cutMessages(count int) (head, rest *PublicBatch) { // explicit 0 need for prevent typos, when type slice[count:count] instead of slice[:count:count] head, _ = newBatch(m.commitRange.partitionSession, m.Messages[0:count:count]) rest, _ = newBatch(m.commitRange.partitionSession, m.Messages[count:]) + return head, rest } } @@ -175,9 +177,11 @@ func splitBytesByMessagesInBatches(batches []*PublicBatch, totalBytesCount int) case want >= restBytes: res := restBytes restBytes = 0 + return res default: restBytes -= want + return want } } diff --git a/internal/topic/topicreaderinternal/batcher.go b/internal/topic/topicreaderinternal/batcher.go index 1d6b7ee27..8eaa5eb6b 100644 --- a/internal/topic/topicreaderinternal/batcher.go +++ b/internal/topic/topicreaderinternal/batcher.go @@ -46,6 +46,7 @@ func (b *batcher) Close(err error) error { b.closed = true b.closeErr = err close(b.closeChan) + return nil } @@ -126,6 +127,7 @@ func (o batcherGetOptions) cutBatchItemsHead(items batcherMessageOrderItems) ( head = newBatcherItemBatch(batchHead) rest = items.ReplaceHeadItem(newBatcherItemBatch(batchRest)) + return head, rest, true } @@ -146,6 +148,7 @@ func (o batcherGetOptions) splitBatch(batch *PublicBatch) (head, rest *PublicBat } head, rest = batch.cutMessages(o.MaxCount) + return head, rest, true } @@ -173,7 +176,8 @@ func (b *batcher) Pop(ctx context.Context, opts batcherGetOptions) (_ batcherMes findRes = b.findNeedLock(opts) if findRes.Ok { - b.applyNeedLock(findRes) + b.applyNeedLock(&findRes) + return } }) @@ -271,10 +275,11 @@ func (b *batcher) applyForceFlagToOptions(options batcherGetOptions) batcherGetO res := options res.MinCount = 1 + 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 { @@ -310,6 +315,7 @@ func (items batcherMessageOrderItems) Append(item batcherMessageOrderItem) (batc } else { return nil, err } + return items, nil } @@ -328,6 +334,7 @@ func (items batcherMessageOrderItems) ReplaceHeadItem(item batcherMessageOrderIt res := make(batcherMessageOrderItems, len(items)) res[0] = item copy(res[1:], items[1:]) + return res } diff --git a/internal/topic/topicreaderinternal/batcher_test.go b/internal/topic/topicreaderinternal/batcher_test.go index e048ef9f2..c926e7076 100644 --- a/internal/topic/topicreaderinternal/batcher_test.go +++ b/internal/topic/topicreaderinternal/batcher_test.go @@ -3,6 +3,7 @@ package topicreaderinternal import ( "context" "errors" + "sync/atomic" "testing" "time" @@ -10,7 +11,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicreader" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" ) @@ -282,7 +282,7 @@ func TestBatcher_PopMinIgnored(t *testing.T) { }, }})) - var IgnoreMinRestrictionsOnNextPopDone xatomic.Int64 + var IgnoreMinRestrictionsOnNextPopDone atomic.Int64 go func() { defer IgnoreMinRestrictionsOnNextPopDone.Add(1) @@ -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) }) @@ -502,5 +502,6 @@ func mustNewBatch(session *partitionSession, messages []*PublicMessage) *PublicB if err != nil { panic(err) } + return batch } diff --git a/internal/topic/topicreaderinternal/commit_range.go b/internal/topic/topicreaderinternal/commit_range.go index 0f1554780..c744cd49b 100644 --- a/internal/topic/topicreaderinternal/commit_range.go +++ b/internal/topic/topicreaderinternal/commit_range.go @@ -32,6 +32,7 @@ func (r *CommitRanges) GetCommitsInfo() []trace.TopicReaderStreamCommitInfo { EndOffset: r.ranges[i].commitOffsetEnd.ToInt64(), } } + return res } @@ -45,6 +46,7 @@ func NewCommitRangesFromPublicCommits(ranges []PublicCommitRange) CommitRanges { for i := 0; i < len(res.ranges); i++ { res.ranges[i] = ranges[i].priv } + return res } @@ -84,6 +86,7 @@ func (r *CommitRanges) toPartitionsOffsets() []rawtopicreader.PartitionCommitOff } r.optimize() + return r.toRawPartitionCommitOffset() } @@ -149,6 +152,7 @@ func (r *CommitRanges) toRawPartitionCommitOffset() []rawtopicreader.PartitionCo } partition.Offsets = append(partition.Offsets, offsetsRange) } + return partitionOffsets } diff --git a/internal/topic/topicreaderinternal/commit_range_test.go b/internal/topic/topicreaderinternal/commit_range_test.go index d0d95cb60..2ba8e1e02 100644 --- a/internal/topic/topicreaderinternal/commit_range_test.go +++ b/internal/topic/topicreaderinternal/commit_range_test.go @@ -284,5 +284,6 @@ func TestCommitsToRawPartitionCommitOffset(t *testing.T) { func testNewCommitRanges(commitable ...PublicCommitRangeGetter) *CommitRanges { var res CommitRanges res.Append(commitable...) + return &res } diff --git a/internal/topic/topicreaderinternal/committer.go b/internal/topic/topicreaderinternal/committer.go index 4f614ce14..c3f22849f 100644 --- a/internal/topic/topicreaderinternal/committer.go +++ b/internal/topic/topicreaderinternal/committer.go @@ -62,6 +62,7 @@ func newCommitter(tracer *trace.Topic, lifeContext context.Context, mode PublicC } res.initChannels() res.start() + return res } @@ -100,6 +101,7 @@ func (c *committer) pushCommit(commitRange commitRange) (commitWaiter, error) { c.m.WithLock(func() { if err := c.backgroundWorker.Context().Err(); err != nil { resErr = err + return } @@ -170,6 +172,7 @@ func (c *committer) waitSendTrigger(ctx context.Context) { case <-ctxDone: case <-finish: } + return } @@ -260,6 +263,7 @@ var commitWaiterLastID int64 func newCommitWaiter(session *partitionSession, endOffset rawtopicreader.Offset) commitWaiter { id := atomic.AddInt64(&commitWaiterLastID, 1) + return commitWaiter{ ID: id, Session: session, @@ -272,5 +276,6 @@ func sendCommitMessage(send sendMessageToServerFunc, batch CommitRanges) error { req := &rawtopicreader.CommitOffsetRequest{ CommitOffsets: batch.toPartitionsOffsets(), } + return send(req) } diff --git a/internal/topic/topicreaderinternal/committer_test.go b/internal/topic/topicreaderinternal/committer_test.go index b9545fe1e..7bf444d62 100644 --- a/internal/topic/topicreaderinternal/committer_test.go +++ b/internal/topic/topicreaderinternal/committer_test.go @@ -23,6 +23,7 @@ func TestCommitterCommit(t *testing.T) { c := newTestCommitter(ctx, t) c.send = func(msg rawtopicreader.ClientMessage) error { t.Fatalf("must not call") + return nil } @@ -65,6 +66,7 @@ func TestCommitterCommitAsync(t *testing.T) { CommitOffsets: testNewCommitRanges(&cRange).toPartitionsOffsets(), }, msg) + return nil } require.NoError(t, c.Commit(ctx, cRange)) @@ -97,6 +99,7 @@ func TestCommitterCommitSync(t *testing.T) { }, msg) c.OnCommitNotify(session, cRange.commitOffsetEnd) + return nil } require.NoError(t, c.Commit(ctx, cRange)) @@ -121,6 +124,7 @@ func TestCommitterCommitSync(t *testing.T) { c.mode = CommitModeSync c.send = func(msg rawtopicreader.ClientMessage) error { close(commitSended) + return nil } @@ -201,6 +205,7 @@ func TestCommitterBuffer(t *testing.T) { c.clock = clock c.send = func(msg rawtopicreader.ClientMessage) error { close(sendCalled) + return nil } @@ -229,6 +234,7 @@ func TestCommitterBuffer(t *testing.T) { commitMess := msg.(*rawtopicreader.CommitOffsetRequest) require.Len(t, commitMess.CommitOffsets, 2) close(sendCalled) + return nil } @@ -261,6 +267,7 @@ func TestCommitterBuffer(t *testing.T) { commitMess := msg.(*rawtopicreader.CommitOffsetRequest) require.Len(t, commitMess.CommitOffsets, 4) close(sendCalled) + return nil } c.commits.appendCommitRanges([]commitRange{ @@ -295,6 +302,7 @@ func TestCommitterBuffer(t *testing.T) { commitMess := msg.(*rawtopicreader.CommitOffsetRequest) require.Len(t, commitMess.CommitOffsets, 4) close(sendCalled) + return nil } @@ -330,6 +338,7 @@ func TestCommitterBuffer(t *testing.T) { sendCalled := make(empty.Chan) c.send = func(msg rawtopicreader.ClientMessage) error { close(sendCalled) + return nil } _, err := c.pushCommit(commitRange{partitionSession: &partitionSession{}}) @@ -344,6 +353,7 @@ func TestCommitterBuffer(t *testing.T) { c := newTestCommitter(ctx, t) c.send = func(msg rawtopicreader.ClientMessage) error { t.Fatal() + return nil } c.commitLoopSignal <- empty.Struct{} // to buffer @@ -357,6 +367,7 @@ func TestCommitterBuffer(t *testing.T) { sendCalled := false c.send = func(msg rawtopicreader.ClientMessage) error { sendCalled = true + return nil } c.commits.appendCommitRange(commitRange{partitionSession: &partitionSession{}}) @@ -374,5 +385,6 @@ func newTestCommitter(ctx context.Context, t testing.TB) *committer { require.ErrorIs(t, err, background.ErrAlreadyClosed) } }) + return res } diff --git a/internal/topic/topicreaderinternal/decoders.go b/internal/topic/topicreaderinternal/decoders.go index 21456e97f..87773cdc7 100644 --- a/internal/topic/topicreaderinternal/decoders.go +++ b/internal/topic/topicreaderinternal/decoders.go @@ -34,8 +34,9 @@ func (m *decoderMap) Decode(codec rawtopiccommon.Codec, input io.Reader) (io.Rea if f := m.m[codec]; f != nil { return f(input) } + 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 57aa89c5a..13cc98f0c 100644 --- a/internal/topic/topicreaderinternal/message.go +++ b/internal/topic/topicreaderinternal/message.go @@ -9,14 +9,13 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xbytes" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" ) 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 { @@ -63,6 +62,7 @@ func (m *PublicMessage) UnmarshalTo(dst PublicMessageContentUnmarshaler) error { } m.dataConsumed = true + return callbackOnReaderContent(globalReadMessagePool, m, m.UncompressedSize, dst) } @@ -71,6 +71,7 @@ func (m *PublicMessage) UnmarshalTo(dst PublicMessageContentUnmarshaler) error { // return topicreader.UnexpectedCodec if message compressed with unknown codec func (m *PublicMessage) Read(p []byte) (n int, err error) { m.dataConsumed = true + return m.data.Read(p) } @@ -108,6 +109,7 @@ type PublicMessageBuilder struct { func NewPublicMessageBuilder() *PublicMessageBuilder { res := &PublicMessageBuilder{} res.initMessage() + return res } @@ -128,50 +130,58 @@ func (pmb *PublicMessageBuilder) initMessage() { // Seqno set message Seqno func (pmb *PublicMessageBuilder) Seqno(seqNo int64) *PublicMessageBuilder { pmb.mess.SeqNo = seqNo + return pmb } // CreatedAt set message CreatedAt func (pmb *PublicMessageBuilder) CreatedAt(createdAt time.Time) *PublicMessageBuilder { pmb.mess.CreatedAt = createdAt + return pmb } func (pmb *PublicMessageBuilder) Metadata(metadata map[string][]byte) *PublicMessageBuilder { pmb.mess.Metadata = make(map[string][]byte, len(metadata)) for key, val := range metadata { - pmb.mess.Metadata[key] = xbytes.Clone(val) + pmb.mess.Metadata[key] = bytes.Clone(val) } + return pmb } // MessageGroupID set message MessageGroupID func (pmb *PublicMessageBuilder) MessageGroupID(messageGroupID string) *PublicMessageBuilder { pmb.mess.MessageGroupID = messageGroupID + return pmb } // WriteSessionMetadata set message WriteSessionMetadata func (pmb *PublicMessageBuilder) WriteSessionMetadata(writeSessionMetadata map[string]string) *PublicMessageBuilder { pmb.mess.WriteSessionMetadata = writeSessionMetadata + return pmb } // Offset set message Offset func (pmb *PublicMessageBuilder) Offset(offset int64) *PublicMessageBuilder { pmb.mess.Offset = offset + return pmb } // WrittenAt set message WrittenAt func (pmb *PublicMessageBuilder) WrittenAt(writtenAt time.Time) *PublicMessageBuilder { pmb.mess.WrittenAt = writtenAt + return pmb } // ProducerID set message ProducerID func (pmb *PublicMessageBuilder) ProducerID(producerID string) *PublicMessageBuilder { pmb.mess.ProducerID = producerID + return pmb } @@ -183,12 +193,14 @@ func (pmb *PublicMessageBuilder) DataAndUncompressedSize(data []byte) *PublicMes pmb.mess.dataConsumed = false pmb.mess.rawDataLen = len(copyData) pmb.mess.UncompressedSize = len(copyData) + return pmb } // UncompressedSize set message UncompressedSize func (pmb *PublicMessageBuilder) UncompressedSize(uncompressedSize int) *PublicMessageBuilder { pmb.mess.UncompressedSize = uncompressedSize + return pmb } @@ -211,5 +223,6 @@ func (pmb *PublicMessageBuilder) PartitionID(partitionID int64) { func (pmb *PublicMessageBuilder) Build() *PublicMessage { mess := pmb.mess pmb.initMessage() + return mess } diff --git a/internal/topic/topicreaderinternal/message_content_pool.go b/internal/topic/topicreaderinternal/message_content_pool.go index 5554cf17b..73b561cae 100644 --- a/internal/topic/topicreaderinternal/message_content_pool.go +++ b/internal/topic/topicreaderinternal/message_content_pool.go @@ -63,5 +63,6 @@ func callbackOnReaderContent( if err := consumer.UnmarshalYDBTopicMessage(buf.Bytes()); err != nil { return xerrors.WithStackTrace(fmt.Errorf("ydb: error unmarshal data: %w", err)) } + return nil } diff --git a/internal/topic/topicreaderinternal/message_content_pool_test.go b/internal/topic/topicreaderinternal/message_content_pool_test.go index 63b13df97..60a6477f5 100644 --- a/internal/topic/topicreaderinternal/message_content_pool_test.go +++ b/internal/topic/topicreaderinternal/message_content_pool_test.go @@ -48,6 +48,7 @@ func TestCallbackOnReaderContent(t *testing.T) { err := callbackOnReaderContent(p, newReader(), 0, testFuncConsumer(func(data []byte) error { called = true require.Equal(t, expectedData, data) + return nil })) require.NoError(t, err) @@ -76,6 +77,7 @@ func TestCallbackOnReaderContent(t *testing.T) { // first call with empty reader - for check internal capacity without reallocation _ = callbackOnReaderContent(p, ErrReader(io.EOF), estimatedSize, testFuncConsumer(func(data []byte) error { require.Empty(t, data) + return nil })) @@ -84,6 +86,7 @@ func TestCallbackOnReaderContent(t *testing.T) { err := callbackOnReaderContent(p, newReader(), estimatedSize, testFuncConsumer(func(data []byte) error { require.Equal(t, expectedData, data) called = true + return nil })) require.NoError(t, err) @@ -103,6 +106,7 @@ func TestCallbackOnReaderContent(t *testing.T) { err := callbackOnReaderContent(p, newReader(), maxInitialBufferSize+10, testFuncConsumer(func(data []byte) error { require.Equal(t, expectedData, data) called = true + return nil })) require.NoError(t, err) @@ -121,6 +125,7 @@ func TestCallbackOnReaderContent(t *testing.T) { err := callbackOnReaderContent(p, newReader(), 0, testFuncConsumer(func(data []byte) error { require.Equal(t, expectedData, data) called = true + return nil })) require.NoError(t, err) diff --git a/internal/topic/topicreaderinternal/partition_session.go b/internal/topic/topicreaderinternal/partition_session.go index 9eca843bb..9e23c5495 100644 --- a/internal/topic/topicreaderinternal/partition_session.go +++ b/internal/topic/topicreaderinternal/partition_session.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "sync" + "sync/atomic" "time" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicreader" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" ) @@ -28,8 +28,8 @@ type partitionSession struct { ctxCancel context.CancelFunc partitionSessionID rawtopicreader.PartitionSessionID - lastReceivedOffsetEndVal xatomic.Int64 - committedOffsetVal xatomic.Int64 + lastReceivedOffsetEndVal atomic.Int64 + committedOffsetVal atomic.Int64 } func newPartitionSession( @@ -54,6 +54,7 @@ func newPartitionSession( } res.committedOffsetVal.Store(committedOffset.ToInt64()) res.lastReceivedOffsetEndVal.Store(committedOffset.ToInt64() - 1) + return res } @@ -70,6 +71,7 @@ func (s *partitionSession) committedOffset() rawtopicreader.Offset { var res rawtopicreader.Offset res.FromInt64(v) + return res } @@ -82,6 +84,7 @@ func (s *partitionSession) lastReceivedMessageOffset() rawtopicreader.Offset { var res rawtopicreader.Offset res.FromInt64(v) + return res } @@ -112,6 +115,7 @@ func (c *partitionSessionStorage) Add(session *partitionSession) error { return xerrors.WithStackTrace(fmt.Errorf("session id already existed: %v", session.partitionSessionID)) } c.sessions[session.partitionSessionID] = &sessionInfo{Session: session} + return nil } @@ -135,6 +139,7 @@ func (c *partitionSessionStorage) Remove(id partitionSessionID) (*partitionSessi c.removeIndex++ if partitionInfo, ok := c.sessions[id]; ok { partitionInfo.RemoveTime = now + return partitionInfo.Session, nil } diff --git a/internal/topic/topicreaderinternal/reader.go b/internal/topic/topicreaderinternal/reader.go index 31144d6fc..b499221d1 100644 --- a/internal/topic/topicreaderinternal/reader.go +++ b/internal/topic/topicreaderinternal/reader.go @@ -73,6 +73,7 @@ type readExplicitMessagesCount int func (count readExplicitMessagesCount) Apply(options ReadMessageBatchOptions) ReadMessageBatchOptions { options.MinCount = int(count) options.MaxCount = int(count) + return options } @@ -257,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) } } @@ -276,5 +277,6 @@ type PublicReadSelector struct { // Clone create deep clone of the selector func (s PublicReadSelector) Clone() *PublicReadSelector { //nolint:gocritic s.Partitions = clone.Int64Slice(s.Partitions) + return &s } diff --git a/internal/topic/topicreaderinternal/reader_test.go b/internal/topic/topicreaderinternal/reader_test.go index 56f7e1de9..4b73a9559 100644 --- a/internal/topic/topicreaderinternal/reader_test.go +++ b/internal/topic/topicreaderinternal/reader_test.go @@ -62,6 +62,7 @@ func TestReader_Close(t *testing.T) { callCompleted: make(empty.Chan), } allStates = append(allStates, state) + return state } diff --git a/internal/topic/topicreaderinternal/stream_reader_impl.go b/internal/topic/topicreaderinternal/stream_reader_impl.go index b85899040..381ccd690 100644 --- a/internal/topic/topicreaderinternal/stream_reader_impl.go +++ b/internal/topic/topicreaderinternal/stream_reader_impl.go @@ -9,13 +9,13 @@ import ( "math/big" "reflect" "runtime/pprof" + "sync/atomic" "time" "github.com/ydb-platform/ydb-go-sdk/v3/credentials" "github.com/ydb-platform/ydb-go-sdk/v3/internal/background" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicreader" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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" @@ -25,8 +25,7 @@ import ( 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 @@ -37,7 +36,7 @@ type topicStreamReaderImpl struct { cancel context.CancelFunc freeBytes chan int - restBufferSizeBytes xatomic.Int64 + restBufferSizeBytes atomic.Int64 sessionController partitionSessionStorage backgroundWorkers background.Worker @@ -160,6 +159,7 @@ func newTopicStreamReaderStopped( res.committer.BufferCountTrigger = cfg.CommitterBatchCounterTrigger res.sessionController.init() res.freeBytes <- cfg.BufferSizeProtoBytes + return res } @@ -167,6 +167,7 @@ func (r *topicStreamReaderImpl) WaitInit(_ context.Context) error { if !r.started { return errors.New("not started: can be started only after initialize from constructor") } + return nil } @@ -263,6 +264,7 @@ func (r *topicStreamReaderImpl) consumeRawMessageFromBuffer(ctx context.Context) case *rawtopicreader.StartPartitionSessionRequest: if err := r.onStartPartitionSessionRequestFromBuffer(m); err != nil { _ = r.CloseWithError(ctx, err) + return } case *rawtopicreader.StopPartitionSessionRequest: @@ -270,6 +272,7 @@ func (r *topicStreamReaderImpl) consumeRawMessageFromBuffer(ctx context.Context) _ = r.CloseWithError(ctx, xerrors.WithStackTrace( fmt.Errorf("ydb: unexpected error on stop partition handler: %w", err), )) + return } case *rawtopicreader.PartitionSessionStatusResponse: @@ -366,6 +369,7 @@ func (r *topicStreamReaderImpl) Commit(ctx context.Context, commitRange commitRa if err = r.checkCommitRange(commitRange); err != nil { return err } + return r.committer.Commit(ctx, commitRange) } @@ -380,7 +384,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) @@ -400,6 +404,7 @@ func (r *topicStreamReaderImpl) send(msg rawtopicreader.ClientMessage) error { trace.TopicOnReaderError(r.cfg.Trace, r.readConnectionID, err) _ = r.CloseWithError(r.ctx, err) } + return err } @@ -426,6 +431,7 @@ func (r *topicStreamReaderImpl) setStarted() error { } r.started = true + return nil } @@ -465,6 +471,7 @@ func (r *topicStreamReaderImpl) addRestBufferBytes(delta int) int { if val <= 0 { r.batcher.IgnoreMinRestrictionsOnNextPop() } + return int(val) } @@ -487,6 +494,7 @@ func (r *topicStreamReaderImpl) readMessagesLoop(ctx context.Context) { continue } _ = r.CloseWithError(ctx, err) + return } @@ -507,16 +515,19 @@ func (r *topicStreamReaderImpl) readMessagesLoop(ctx context.Context) { case *rawtopicreader.StartPartitionSessionRequest: if err = r.onStartPartitionSessionRequest(m); err != nil { _ = r.CloseWithError(ctx, err) + return } case *rawtopicreader.StopPartitionSessionRequest: if err = r.onStopPartitionSessionRequest(m); err != nil { _ = r.CloseWithError(ctx, err) + return } case *rawtopicreader.CommitOffsetResponse: if err = r.onCommitResponse(m); err != nil { _ = r.CloseWithError(ctx, err) + return } @@ -546,6 +557,7 @@ func (r *topicStreamReaderImpl) dataRequestLoop(ctx context.Context) { select { case <-doneChan: _ = r.CloseWithError(ctx, r.ctx.Err()) + return case free := <-r.freeBytes: @@ -686,6 +698,7 @@ func (r *topicStreamReaderImpl) CloseWithError(ctx context.Context, reason error if closeErr == nil { closeErr = bgCloseErr } + return closeErr } @@ -741,6 +754,7 @@ func (r *topicStreamReaderImpl) onStartPartitionSessionRequest(m *rawtopicreader if err := r.sessionController.Add(session); err != nil { return err } + return r.batcher.PushRawMessage(session, m) } diff --git a/internal/topic/topicreaderinternal/stream_reader_impl_test.go b/internal/topic/topicreaderinternal/stream_reader_impl_test.go index 4cf05a21c..a518c2058 100644 --- a/internal/topic/topicreaderinternal/stream_reader_impl_test.go +++ b/internal/topic/topicreaderinternal/stream_reader_impl_test.go @@ -391,6 +391,7 @@ func TestStreamReaderImpl_OnPartitionCloseHandle(t *testing.T) { require.NoError(t, info.PartitionContext.Err()) readMessagesCtxCancel() + return nil } @@ -434,6 +435,7 @@ func TestStreamReaderImpl_OnPartitionCloseHandle(t *testing.T) { require.Error(t, info.PartitionContext.Err()) readMessagesCtxCancel() + return nil } @@ -577,6 +579,7 @@ func TestTopicStreamReaderImpl_ReadMessages(t *testing.T) { _, err := writer.Write([]byte(msg)) require.NoError(t, writer.Close()) require.NoError(t, err) + return b.Bytes() } @@ -842,6 +845,7 @@ func TestTopicStreamReadImpl_BatchReaderWantMoreMessagesThenBufferCanHold(t *tes }, }, }) + return nextDataRequested } @@ -935,6 +939,7 @@ func TestTopicStreamReadImpl_CommitWithBadSession(t *testing.T) { sleep() require.False(t, e.reader.closed) + return commitErr } t.Run("CommitModeNone", func(t *testing.T) { @@ -1109,6 +1114,7 @@ readMessages: e.m.WithLock(func() { e.nextMessageNeedCallback = res.nextMessageCallback }) + return res.msg, res.err } } diff --git a/internal/topic/topicreaderinternal/stream_reconnector.go b/internal/topic/topicreaderinternal/stream_reconnector.go index 6bae747f0..393b0ecea 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 @@ -115,6 +109,7 @@ func (r *readerReconnector) ReadMessageBatch(ctx context.Context, opts ReadMessa case r.isRetriableError(err): r.fireReconnectOnRetryableError(stream, err) runtime.Gosched() + continue case err != nil: return nil, err @@ -126,8 +121,10 @@ func (r *readerReconnector) ReadMessageBatch(ctx context.Context, opts ReadMessa if r.isRetriableError(err) { r.fireReconnectOnRetryableError(stream, err) runtime.Gosched() + continue } + return res, err } } @@ -140,6 +137,7 @@ func (r *readerReconnector) Commit(ctx context.Context, commitRange commitRange) err = stream.Commit(ctx, commitRange) r.fireReconnectOnRetryableError(stream, err) + return err } @@ -166,6 +164,7 @@ func (r *readerReconnector) CloseWithError(ctx context.Context, err error) error } }) }) + return closeErr } @@ -292,11 +291,13 @@ func (r *readerReconnector) reconnect(ctx context.Context, reason error, oldRead } } }) + return err } func (r *readerReconnector) isRetriableError(err error) bool { _, res := topic.CheckRetryMode(err, r.retrySettings, 0) + return res } @@ -341,6 +342,7 @@ func (r *readerReconnector) connectWithTimeout() (_ batchedStreamReader, err err if res.err == nil { return res.stream, nil } + return nil, res.err } @@ -383,6 +385,7 @@ func (r *readerReconnector) stream(ctx context.Context) (batchedStreamReader, er connectionChan = r.streamConnectionInProgress if r.closedErr != nil { err = r.closedErr + return } }) @@ -402,6 +405,7 @@ func (r *readerReconnector) stream(ctx context.Context) (batchedStreamReader, er err = r.streamErr }) r.fireReconnectOnRetryableError(reader, err) + return reader, err } } diff --git a/internal/topic/topicreaderinternal/stream_reconnector_test.go b/internal/topic/topicreaderinternal/stream_reconnector_test.go index 57b0f00dc..d9034066e 100644 --- a/internal/topic/topicreaderinternal/stream_reconnector_test.go +++ b/internal/topic/topicreaderinternal/stream_reconnector_test.go @@ -61,6 +61,7 @@ func TestTopicReaderReconnectorReadMessageBatch(t *testing.T) { if connectCalled > 1 { return nil, errors.New("unexpected call test connect function") } + return baseReader, nil }, streamErr: errUnconnected, @@ -103,6 +104,7 @@ func TestTopicReaderReconnectorReadMessageBatch(t *testing.T) { reader := &readerReconnector{ readerConnect: func(ctx context.Context) (batchedStreamReader, error) { connectCalled++ + return readers[connectCalled-1], nil }, streamErr: errUnconnected, @@ -218,6 +220,7 @@ func TestTopicReaderReconnectorConnectionLoop(t *testing.T) { { callback: func(ctx context.Context) (batchedStreamReader, error) { close(stream1Ready) + return newStream1, nil }, }, @@ -227,12 +230,14 @@ func TestTopicReaderReconnectorConnectionLoop(t *testing.T) { { callback: func(ctx context.Context) (batchedStreamReader, error) { close(stream2Ready) + return newStream2, nil }, }, { callback: func(ctx context.Context) (batchedStreamReader, error) { t.Fatal() + return nil, errors.New("unexpected call") }, }, @@ -288,10 +293,12 @@ func TestTopicReaderReconnectorStart(t *testing.T) { reconnector.readerConnect = readerConnectFuncMock([]readerConnectFuncAnswer{ {callback: func(ctx context.Context) (batchedStreamReader, error) { close(connectionRequested) + return stream, nil }}, {callback: func(ctx context.Context) (batchedStreamReader, error) { t.Error() + return nil, errors.New("unexpected call") }}, }...) @@ -345,6 +352,7 @@ func TestTopicReaderReconnectorWaitInit(t *testing.T) { reconnector.readerConnect = readerConnectFuncMock(readerConnectFuncAnswer{ callback: func(ctx context.Context) (batchedStreamReader, error) { cancel() + return stream, nil }, }) diff --git a/internal/topic/topicwriterinternal/encoders.go b/internal/topic/topicwriterinternal/encoders.go index 60eb3e2dc..7ae89d534 100644 --- a/internal/topic/topicwriterinternal/encoders.go +++ b/internal/topic/topicwriterinternal/encoders.go @@ -42,6 +42,7 @@ func (e *EncoderMap) CreateLazyEncodeWriter(codec rawtopiccommon.Codec, target i if encoderCreator, ok := e.m[codec]; ok { return encoderCreator(target) } + return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf("ydb: unexpected codec '%v' for encode message", codec))) } @@ -50,11 +51,13 @@ func (e *EncoderMap) GetSupportedCodecs() rawtopiccommon.SupportedCodecs { for codec := range e.m { res = append(res, codec) } + return res } func (e *EncoderMap) IsSupported(codec rawtopiccommon.Codec) bool { _, ok := e.m[codec] + return ok } @@ -122,6 +125,7 @@ func (s *EncoderSelector) CompressMessages(messages []messageWithDataContent) (r err = cacheMessages(messages, codec, s.parallelCompressors) onCompressDone(err) } + return codec, err } @@ -257,6 +261,7 @@ func cacheMessages(messages []messageWithDataContent, codec rawtopiccommon.Codec resErrMutex.WithLock(func() { resErr = localErr }) + return } } diff --git a/internal/topic/topicwriterinternal/message.go b/internal/topic/topicwriterinternal/message.go index 9b1088b95..5c618e631 100644 --- a/internal/topic/topicwriterinternal/message.go +++ b/internal/topic/topicwriterinternal/message.go @@ -9,7 +9,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xbytes" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" ) @@ -38,6 +37,7 @@ func (p PublicFuturePartitioning) ToRaw() rawtopicwriter.Partitioning { if p.hasPartitionID { return rawtopicwriter.NewPartitioningPartitionID(p.partitionID) } + return rawtopicwriter.NewPartitioningMessageGroup(p.messageGroupID) } @@ -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) { @@ -85,7 +85,7 @@ func (m *messageWithDataContent) cacheMetadata() { if len(m.Metadata) > 0 { ownCopy := make(map[string][]byte, len(m.Metadata)) for key, val := range m.Metadata { - ownCopy[key] = xbytes.Clone(val) + ownCopy[key] = bytes.Clone(val) } m.Metadata = ownCopy } else { @@ -97,6 +97,7 @@ func (m *messageWithDataContent) cacheMetadata() { func (m *messageWithDataContent) CacheMessageData(codec rawtopiccommon.Codec) error { m.cacheMetadata() _, err := m.GetEncodedBytes(codec) + return err } @@ -128,6 +129,7 @@ func (m *messageWithDataContent) encodeRawContent(codec rawtopiccommon.Codec) ([ } m.bufCodec = codec + return m.bufEncoded.Bytes(), nil } @@ -142,6 +144,7 @@ func (m *messageWithDataContent) readDataToRawBuf() error { m.BufUncompressedSize = int(writtenBytes) m.Data = nil } + return nil } @@ -173,6 +176,7 @@ func (m *messageWithDataContent) readDataToTargetCodec(codec rawtopiccommon.Code } m.BufUncompressedSize = int(bytesCount) m.Data = nil + return nil } @@ -188,6 +192,7 @@ func (m *messageWithDataContent) getRawBytes() ([]byte, error) { if err != nil { return nil, err } + return m.rawBuf.Bytes(), nil } @@ -204,6 +209,7 @@ func (m *messageWithDataContent) getEncodedBytes(codec rawtopiccommon.Codec) ([] if err != nil { return nil, err } + return m.bufEncoded.Bytes(), nil } } diff --git a/internal/topic/topicwriterinternal/queue.go b/internal/topic/topicwriterinternal/queue.go index ab5ff7e56..25ff072d3 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")) @@ -57,6 +58,7 @@ func newMessageQueue() messageQueue { func (q *messageQueue) AddMessages(messages []messageWithDataContent) error { _, err := q.addMessages(messages, false) + return err } @@ -136,6 +138,7 @@ func (q *messageQueue) addMessageNeedLock( q.messagesByOrder[messageIndex] = mess q.seqNoToOrderID[mess.SeqNo] = messageIndex q.lastSeqNo = mess.SeqNo + return messageIndex } @@ -149,6 +152,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 { @@ -158,6 +164,7 @@ func (q *messageQueue) AcksReceived(acks []rawtopicwriter.WriteAck) error { } q.acksReceivedEvent.Broadcast() + return nil } @@ -169,6 +176,7 @@ func (q *messageQueue) ackReceivedNeedLock(seqNo int64) error { delete(q.seqNoToOrderID, seqNo) delete(q.messagesByOrder, orderID) + return nil } @@ -270,6 +278,7 @@ func (q *messageQueue) getMessagesForSendWithLock() []messageWithDataContent { res = append(res, msg) } } + return res } @@ -288,6 +297,7 @@ func (q *messageQueue) Wait(ctx context.Context, waiter MessageQueueAckWaiter) e checkMessageIndex := waiter.sequenseNumbers[0] if _, ok := q.messagesByOrder[checkMessageIndex]; ok { hasWaited = true + return } waiter.sequenseNumbers = waiter.sequenseNumbers[1:] diff --git a/internal/topic/topicwriterinternal/queue_test.go b/internal/topic/topicwriterinternal/queue_test.go index af2c26033..d62e6b16e 100644 --- a/internal/topic/topicwriterinternal/queue_test.go +++ b/internal/topic/topicwriterinternal/queue_test.go @@ -14,7 +14,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" ) @@ -142,7 +141,7 @@ func TestMessageQueue_GetMessages(t *testing.T) { }() readFinished := make(empty.Chan) - var lastReadSeqNo xatomic.Int64 + var lastReadSeqNo atomic.Int64 readCtx, readCancel := xcontext.WithCancel(ctx) defer readCancel() @@ -159,6 +158,7 @@ func TestMessageQueue_GetMessages(t *testing.T) { for _, mess := range messages { if lastReadSeqNo.Load()+1 != mess.SeqNo { fatalChan <- string(debug.Stack()) + return } lastReadSeqNo.Store(mess.SeqNo) @@ -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() @@ -491,5 +511,6 @@ func getSeqNumbers(messages []messageWithDataContent) []int64 { for i := range messages { res = append(res, messages[i].SeqNo) } + return res } diff --git a/internal/topic/topicwriterinternal/writer_grpc_mock_test.go b/internal/topic/topicwriterinternal/writer_grpc_mock_test.go new file mode 100644 index 000000000..1a977cb75 --- /dev/null +++ b/internal/topic/topicwriterinternal/writer_grpc_mock_test.go @@ -0,0 +1,134 @@ +package topicwriterinternal_test + +import ( + "errors" + "fmt" + "strings" + "testing" + + "github.com/rekby/fixenv" + "github.com/rekby/fixenv/sf" + "github.com/stretchr/testify/require" + "github.com/ydb-platform/ydb-go-genproto/Ydb_Topic_V1" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Issue" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Topic" + + "github.com/ydb-platform/ydb-go-sdk/v3" + "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" + "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions" + "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicwriter" +) + +func TestRegressionOperationUnavailableIssue1007(t *testing.T) { + xtest.TestManyTimes(t, func(t testing.TB) { + e := fixenv.New(t) + + mock := newTopicWriterOperationUnavailable() + connString := xtest.GrpcMockTopicConnString(e, mock) + + db, err := ydb.Open(sf.Context(e), connString) + require.NoError(t, err) + + writer, err := db.Topic().StartWriter("test", topicoptions.WithWriterWaitServerAck(true)) + require.NoError(t, err) + + err = writer.Write(sf.Context(e), topicwriter.Message{ + Data: strings.NewReader("asd"), + }) + require.NoError(t, err) + require.True(t, mock.UnavailableResponsed) + }) +} + +type topicWriterOperationUnavailable struct { + Ydb_Topic_V1.UnimplementedTopicServiceServer + + UnavailableResponsed bool +} + +func newTopicWriterOperationUnavailable() *topicWriterOperationUnavailable { + return &topicWriterOperationUnavailable{} +} + +func (t *topicWriterOperationUnavailable) StreamWrite(server Ydb_Topic_V1.TopicService_StreamWriteServer) error { + initMsg, err := server.Recv() + if err != nil { + return fmt.Errorf("failed read init message: %w", err) + } + + if initMsg.GetInitRequest() == nil { + return errors.New("first message must be init message") + } + + err = server.Send(&Ydb_Topic.StreamWriteMessage_FromServer{ + Status: Ydb.StatusIds_SUCCESS, + ServerMessage: &Ydb_Topic.StreamWriteMessage_FromServer_InitResponse{ + InitResponse: &Ydb_Topic.StreamWriteMessage_InitResponse{ + LastSeqNo: 0, + SessionId: "test", + PartitionId: 0, + SupportedCodecs: nil, + }, + }, + }) + if err != nil { + return fmt.Errorf("failed to send init response: %w", err) + } + + if !t.UnavailableResponsed { + t.UnavailableResponsed = true + + err = server.Send(&Ydb_Topic.StreamWriteMessage_FromServer{ + Status: Ydb.StatusIds_UNAVAILABLE, + Issues: []*Ydb_Issue.IssueMessage{ + { + Message: "Test status unavailable", + }, + }, + }) + + if err != nil { + return fmt.Errorf("failed to send error response: %w", err) + } + + return nil + } + + // wait message block + messagesMsg, err := server.Recv() + if err != nil { + return errors.New("failed to read messages block") + } + + if len(messagesMsg.GetClientMessage().(*Ydb_Topic.StreamWriteMessage_FromClient_WriteRequest). + WriteRequest.GetMessages()) == 0 { + return errors.New("received zero messages block") + } + + err = server.Send(&Ydb_Topic.StreamWriteMessage_FromServer{ + Status: Ydb.StatusIds_SUCCESS, + ServerMessage: &Ydb_Topic.StreamWriteMessage_FromServer_WriteResponse{ + WriteResponse: &Ydb_Topic.StreamWriteMessage_WriteResponse{ + Acks: []*Ydb_Topic.StreamWriteMessage_WriteResponse_WriteAck{ + { + SeqNo: 1, + MessageWriteStatus: &Ydb_Topic.StreamWriteMessage_WriteResponse_WriteAck_Written_{ + Written: &Ydb_Topic.StreamWriteMessage_WriteResponse_WriteAck_Written{ + Offset: 1, + }, + }, + }, + }, + PartitionId: 0, + WriteStatistics: &Ydb_Topic.StreamWriteMessage_WriteResponse_WriteStatistics{}, + }, + }, + }) + + if err != nil { + return fmt.Errorf("failed to sent write ack: %w", err) + } + + return nil +} diff --git a/internal/topic/topicwriterinternal/writer_reconnector.go b/internal/topic/topicwriterinternal/writer_reconnector.go index fcd1eabf7..dd7c1b72d 100644 --- a/internal/topic/topicwriterinternal/writer_reconnector.go +++ b/internal/topic/topicwriterinternal/writer_reconnector.go @@ -8,6 +8,7 @@ import ( "math" "math/big" "runtime" + "sync/atomic" "time" "github.com/google/uuid" @@ -22,7 +23,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter" "github.com/ydb-platform/ydb-go-sdk/v3/internal/topic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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" @@ -69,6 +69,7 @@ func (cfg *WriterReconnectorConfig) validate() error { cfg.producerID != cfg.defaultPartitioning.MessageGroupID { return xerrors.WithStackTrace(errProducerIDNotEqualMessageGroupID) } + return nil } @@ -113,25 +114,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 xatomic.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( @@ -139,6 +137,7 @@ func newWriterReconnector( ) *WriterReconnector { res := newWriterReconnectorStopped(cfg) res.start() + return res } @@ -288,6 +287,7 @@ func (w *WriterReconnector) checkMessages(messages []messageWithDataContent) err return xerrors.WithStackTrace(fmt.Errorf("message size bytes %v: %w", size, errLargeMessage)) } } + return nil } @@ -321,6 +321,7 @@ func (w *WriterReconnector) createMessagesWithContent(messages []PublicMessage) if err != nil { return nil, err } + return res, nil } @@ -339,6 +340,7 @@ func (w *WriterReconnector) close(ctx context.Context, reason error) (resErr err if resErr == nil { resErr = bgErr } + return resErr } @@ -348,7 +350,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 @@ -369,7 +371,7 @@ func (w *WriterReconnector) connectionLoop(ctx context.Context) { streamCtx, streamCtxCancel = createStreamContext() now := time.Now() - if topic.CheckResetReconnectionCounters(prevAttemptTime, now, w.cfg.connectTimeout) { + if startOfRetries.IsZero() || topic.CheckResetReconnectionCounters(prevAttemptTime, now, w.cfg.connectTimeout) { attempt = 0 startOfRetries = w.clock.Now() } else { @@ -388,6 +390,7 @@ func (w *WriterReconnector) connectionLoop(ctx context.Context) { } } else { _ = w.close(ctx, reconnectReason) + return } } @@ -424,11 +427,13 @@ func (w *WriterReconnector) startWriteStream(ctx, streamCtx context.Context, att } w.queue.ResetSentProgress() + return NewSingleStreamWriter(ctx, w.createWriterStreamConfig(stream)) } func (w *WriterReconnector) needReceiveLastSeqNo() bool { - res := w.cfg.AutoSetSeqNo && !w.firstConnectionHandled.Load() + res := !w.firstConnectionHandled.Load() + return res } @@ -462,11 +467,13 @@ func (w *WriterReconnector) connectWithTimeout(streamLifetimeContext context.Con select { case <-timer.C: connectCancel() + return nil, xerrors.WithStackTrace(errConnTimeout) case res := <-resCh: // force no cancel connect context - because it will break stream // context will cancel by cancel streamLifetimeContext while reconnect or stop connection _ = connectCancel + return res.stream, res.err } } @@ -480,6 +487,7 @@ func (w *WriterReconnector) onWriterChange(writerStream *SingleStreamWriter) { w.m.WithLock(func() { if writerStream == nil { w.sessionID = "" + return } w.sessionID = writerStream.SessionID @@ -490,7 +498,7 @@ func (w *WriterReconnector) onWriterChange(writerStream *SingleStreamWriter) { defer close(w.firstInitResponseProcessedChan) isFirstInit = true - if w.cfg.AutoSetSeqNo { + if writerStream.LastSeqNumRequested { w.lastSeqNo = writerStream.ReceivedLastSeqNum } }) @@ -561,6 +569,7 @@ func (w *WriterReconnector) createWriterStreamConfig(stream RawTopicWriterStream w.needReceiveLastSeqNo(), w.writerInstanceID, ) + return cfg } @@ -581,6 +590,7 @@ func sendMessagesToStream( if err != nil { return xerrors.WithStackTrace(fmt.Errorf("ydb: failed send write request: %w", err)) } + return nil } @@ -614,6 +624,7 @@ func splitMessagesByBufCodec(messages []messageWithDataContent) (res [][]message } } res = append(res, messages[currentGroupStart:len(messages):len(messages)]) + return res } @@ -674,6 +685,7 @@ func calculateAllowedCodecs(forceCodec rawtopiccommon.Codec, encoderMap *Encoder if serverCodecs.AllowedByCodecsList(forceCodec) && encoderMap.IsSupported(forceCodec) { return rawtopiccommon.SupportedCodecs{forceCodec} } + return nil } @@ -692,6 +704,7 @@ func calculateAllowedCodecs(forceCodec rawtopiccommon.Codec, encoderMap *Encoder if len(res) == 0 { res = nil } + return res } @@ -702,5 +715,6 @@ func createPublicCodecsFromRaw(codecs rawtopiccommon.SupportedCodecs) []topictyp for i, v := range codecs { res[i] = topictypes.Codec(v) } + return res } diff --git a/internal/topic/topicwriterinternal/writer_reconnector_test.go b/internal/topic/topicwriterinternal/writer_reconnector_test.go index 618e61706..4bf18c6ff 100644 --- a/internal/topic/topicwriterinternal/writer_reconnector_test.go +++ b/internal/topic/topicwriterinternal/writer_reconnector_test.go @@ -20,7 +20,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawydb" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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/xtest" @@ -282,6 +281,7 @@ func TestWriterReconnector_Write_QueueLimit(t *testing.T) { waitStartQueueWait := func(targetWaiters int) { xtest.SpinWaitCondition(t, nil, func() bool { res := getWaitersCount(w.semaphore) == targetWaiters + return res }) } @@ -328,8 +328,9 @@ func TestWriterImpl_InitSession(t *testing.T) { sessionID := "test-session-id" w.onWriterChange(&SingleStreamWriter{ - ReceivedLastSeqNum: lastSeqNo, - SessionID: sessionID, + ReceivedLastSeqNum: lastSeqNo, + LastSeqNumRequested: true, + SessionID: sessionID, }) require.Equal(t, sessionID, w.sessionID) @@ -344,7 +345,8 @@ func TestWriterImpl_WaitInit(t *testing.T) { LastSeqNum: int64(123), } w.onWriterChange(&SingleStreamWriter{ - ReceivedLastSeqNum: expectedInitData.LastSeqNum, + ReceivedLastSeqNum: expectedInitData.LastSeqNum, + LastSeqNumRequested: true, }) initData, err := w.WaitInit(context.Background()) @@ -405,6 +407,7 @@ func TestWriterImpl_Reconnect(t *testing.T) { close(connectCalledChan) connectCalled = true require.NotEqual(t, ctx, streamCtxArg) + return strm, nil } @@ -432,9 +435,15 @@ func TestWriterImpl_Reconnect(t *testing.T) { connectionError error } + isFirstConnection := true newStream := func(name string) *MockRawTopicWriterStream { strm := NewMockRawTopicWriterStream(mc) initReq := testCreateInitRequest(w) + if isFirstConnection { + isFirstConnection = false + } else { + initReq.GetLastSeqNo = false + } streamClosed := make(empty.Chan) strm.EXPECT().CloseSend().Do(func() { @@ -458,6 +467,7 @@ func TestWriterImpl_Reconnect(t *testing.T) { xtest.WaitChannelClosed(t, streamClosed) t.Logf("channel closed: %v", name) }).Return(nil, errors.New("test stream closed")).MaxTimes(1) + return strm } @@ -497,7 +507,7 @@ func TestWriterImpl_Reconnect(t *testing.T) { }, } - var connectionAttempt xatomic.Int64 + var connectionAttempt atomic.Int64 w.cfg.Connect = func(ctx context.Context) (RawTopicWriterStream, error) { attemptIndex := int(connectionAttempt.Add(1)) - 1 t.Logf("connect with attempt index: %v", attemptIndex) @@ -516,7 +526,7 @@ func TestWriterImpl_Reconnect(t *testing.T) { err := w.Write(ctx, newTestMessages(1)) require.NoError(t, err) - xtest.WaitChannelClosed(t, connectionLoopStopped) + xtest.WaitChannelClosedWithTimeout(t, connectionLoopStopped, 4*time.Second) }) } @@ -750,6 +760,7 @@ func TestCalculateAllowedCodecs(t *testing.T) { func newTestMessageWithDataContent(num int) messageWithDataContent { res := newMessageDataWithContent(PublicMessage{SeqNo: int64(num)}, testCommonEncoders) + return res } @@ -758,6 +769,7 @@ func newTestMessages(numbers ...int) []PublicMessage { for i, num := range numbers { messages[i].SeqNo = int64(num) } + return messages } @@ -766,6 +778,7 @@ func newTestMessagesWithContent(numbers ...int) []messageWithDataContent { for _, num := range numbers { messages = append(messages, newTestMessageWithDataContent(num)) } + return messages } @@ -800,6 +813,7 @@ func isClosed(ch <-chan struct{}) bool { if existVal { panic("value, when not expected") } + return true default: return false @@ -843,6 +857,7 @@ func newTestEnv(t testing.TB, options *testEnvOptions) *testEnv { if connectNum > 1 { t.Fatalf("test: default env support most one connection") } + return res.stream, nil })) writerOptions = append(writerOptions, options.writerOptions...) @@ -880,6 +895,7 @@ func newTestEnv(t testing.TB, options *testEnvOptions) *testEnv { close(res.stopReadEvents) <-streamClosed }) + return res } @@ -907,5 +923,6 @@ type sendFromServerResponse struct { func testCreateInitRequest(w *WriterReconnector) rawtopicwriter.InitRequest { req := newSingleStreamWriterStopped(context.Background(), w.createWriterStreamConfig(nil)).createInitRequest() + return req } diff --git a/internal/topic/topicwriterinternal/writer_reconnector_unsafe_test.go b/internal/topic/topicwriterinternal/writer_reconnector_unsafe_test.go index 4d0610ff3..0b0d8cd44 100644 --- a/internal/topic/topicwriterinternal/writer_reconnector_unsafe_test.go +++ b/internal/topic/topicwriterinternal/writer_reconnector_unsafe_test.go @@ -24,5 +24,6 @@ func getWaitersCount(sem *semaphore.Weighted) int { waitersField := semVal.FieldByName("waiters") waitersPointer := unsafe.Pointer(waitersField.UnsafeAddr()) waiters := (*list.List)(waitersPointer) + return waiters.Len() } diff --git a/internal/topic/topicwriterinternal/writer_single_stream.go b/internal/topic/topicwriterinternal/writer_single_stream.go index 3d835398d..4f01c56d1 100644 --- a/internal/topic/topicwriterinternal/writer_single_stream.go +++ b/internal/topic/topicwriterinternal/writer_single_stream.go @@ -5,12 +5,12 @@ import ( "errors" "fmt" "reflect" + "sync/atomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/background" "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopiccommon" "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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" @@ -24,7 +24,7 @@ type SingleStreamWriterConfig struct { stream RawTopicWriterStream queue *messageQueue encodersMap *EncoderMap - getAutoSeq bool + getLastSeqNum bool reconnectorInstanceID string } @@ -33,7 +33,7 @@ func newSingleStreamWriterConfig( stream RawTopicWriterStream, queue *messageQueue, encodersMap *EncoderMap, - getAutoSeq bool, + getLastSeqNum bool, reconnectorID string, ) SingleStreamWriterConfig { return SingleStreamWriterConfig{ @@ -41,24 +41,24 @@ func newSingleStreamWriterConfig( stream: stream, queue: queue, encodersMap: encodersMap, - getAutoSeq: getAutoSeq, + getLastSeqNum: getLastSeqNum, reconnectorInstanceID: reconnectorID, } } type SingleStreamWriter struct { - ReceivedLastSeqNum int64 - SessionID string - PartitionID int64 - CodecsFromServer rawtopiccommon.SupportedCodecs - Encoder EncoderSelector - - cfg SingleStreamWriterConfig - allowedCodecs rawtopiccommon.SupportedCodecs - background background.Worker - closed xatomic.Bool - closeReason error - closeCompleted empty.Chan + cfg SingleStreamWriterConfig + Encoder EncoderSelector + background background.Worker + CodecsFromServer rawtopiccommon.SupportedCodecs + allowedCodecs rawtopiccommon.SupportedCodecs + SessionID string + closeReason error + ReceivedLastSeqNum int64 + PartitionID int64 + closeCompleted empty.Chan + closed atomic.Bool + LastSeqNumRequested bool } func NewSingleStreamWriter( @@ -69,9 +69,11 @@ func NewSingleStreamWriter( err := res.initStream() if err != nil { _ = res.close(context.Background(), err) + return nil, err } res.start() + return res, nil } @@ -81,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), } } @@ -152,9 +154,11 @@ func (w *SingleStreamWriter) initStream() (err error) { ) w.SessionID = result.SessionID + w.LastSeqNumRequested = req.GetLastSeqNo w.ReceivedLastSeqNum = result.LastSeqNo w.PartitionID = result.PartitionID w.CodecsFromServer = result.SupportedCodecs + return nil } @@ -164,7 +168,7 @@ func (w *SingleStreamWriter) createInitRequest() rawtopicwriter.InitRequest { ProducerID: w.cfg.producerID, WriteSessionMeta: w.cfg.writerMeta, Partitioning: w.cfg.defaultPartitioning, - GetLastSeqNo: w.cfg.getAutoSeq, + GetLastSeqNo: w.cfg.getLastSeqNum, } } @@ -178,16 +182,18 @@ func (w *SingleStreamWriter) receiveMessagesLoop(ctx context.Context) { if err != nil { err = xerrors.WithStackTrace(fmt.Errorf("ydb: failed to receive message from write stream: %w", err)) _ = w.close(ctx, err) + return } 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() _ = w.close(closeCtx, reason) + return } case *rawtopicwriter.UpdateTokenResponse: @@ -211,12 +217,14 @@ func (w *SingleStreamWriter) sendMessagesFromQueueToStreamLoop(ctx context.Conte messages, err := w.cfg.queue.GetMessagesForSend(ctx) if err != nil { _ = w.close(ctx, err) + return } targetCodec, err := w.Encoder.CompressMessages(messages) if err != nil { _ = w.close(ctx, err) + return } @@ -233,6 +241,7 @@ func (w *SingleStreamWriter) sendMessagesFromQueueToStreamLoop(ctx context.Conte if err != nil { err = xerrors.WithStackTrace(fmt.Errorf("ydb: error send message to topic stream: %w", err)) _ = w.close(ctx, err) + return } } @@ -272,5 +281,6 @@ func (w *SingleStreamWriter) sendUpdateToken(ctx context.Context) (err error) { req := &rawtopicwriter.UpdateTokenRequest{} req.Token = token + return stream.Send(req) } diff --git a/internal/topic/topicwriterinternal/writer_single_stream_test.go b/internal/topic/topicwriterinternal/writer_single_stream_test.go index 59e08bc53..d12d7068b 100644 --- a/internal/topic/topicwriterinternal/writer_single_stream_test.go +++ b/internal/topic/topicwriterinternal/writer_single_stream_test.go @@ -20,7 +20,7 @@ func TestWriterImpl_CreateInitMessage(t *testing.T) { defaultPartitioning: rawtopicwriter.NewPartitioningPartitionID(5), compressorCount: 1, }, - getAutoSeq: true, + getLastSeqNum: true, } w := newSingleStreamWriterStopped(ctx, cfg) expected := rawtopicwriter.InitRequest{ @@ -36,7 +36,7 @@ func TestWriterImpl_CreateInitMessage(t *testing.T) { t.Run("WithoutGetLastSeq", func(t *testing.T) { ctx := xtest.Context(t) w := newSingleStreamWriterStopped(ctx, - SingleStreamWriterConfig{getAutoSeq: false}, + SingleStreamWriterConfig{getLastSeqNum: false}, ) require.False(t, w.createInitRequest().GetLastSeqNo) }) 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/time.go b/internal/value/time.go index b840ddedc..37f7aa374 100644 --- a/internal/value/time.go +++ b/internal/value/time.go @@ -54,6 +54,7 @@ func DatetimeToTime(n uint32) time.Time { func TimestampToTime(n uint64) time.Time { sec := n / 1e6 nsec := (n - (sec * 1e6)) * 1000 + return time.Unix(int64(sec), int64(nsec)) } @@ -70,6 +71,7 @@ func TzDateToTime(s string) (t time.Time, err error) { if err != nil { return t, xerrors.WithStackTrace(fmt.Errorf("parse '%s' failed: %w", s, err)) } + return t, nil } @@ -86,6 +88,7 @@ func TzDatetimeToTime(s string) (t time.Time, err error) { if err != nil { return t, xerrors.WithStackTrace(fmt.Errorf("parse '%s' failed: %w", s, err)) } + return t, nil } @@ -106,5 +109,6 @@ func TzTimestampToTime(s string) (t time.Time, err error) { if err != nil { return t, xerrors.WithStackTrace(fmt.Errorf("parse '%s' failed: %w", s, err)) } + return t, nil } diff --git a/internal/value/time_test.go b/internal/value/time_test.go index f9d93ac37..a1e0cfca2 100644 --- a/internal/value/time_test.go +++ b/internal/value/time_test.go @@ -20,6 +20,7 @@ func TestTzSomeToTime(t *testing.T) { time.Date(2020, time.May, 29, 0, 0, 0, 0, func() *time.Location { l, _ := time.LoadLocation("Europe/Berlin") + return l }(), ), @@ -31,6 +32,7 @@ func TestTzSomeToTime(t *testing.T) { time.Date(2020, time.May, 29, 11, 22, 54, 0, func() *time.Location { l, _ := time.LoadLocation("Europe/Berlin") + return l }(), ), @@ -42,6 +44,7 @@ func TestTzSomeToTime(t *testing.T) { time.Date(2020, time.May, 29, 11, 22, 54, 123456000, func() *time.Location { l, _ := time.LoadLocation("Europe/Berlin") + return l }(), ), @@ -53,6 +56,7 @@ func TestTzSomeToTime(t *testing.T) { time.Date(2020, time.May, 29, 11, 22, 54, 0, func() *time.Location { l, _ := time.LoadLocation("Europe/Berlin") + return l }(), ), diff --git a/internal/value/type.go b/internal/value/type.go deleted file mode 100644 index 11ccb19b1..000000000 --- a/internal/value/type.go +++ /dev/null @@ -1,849 +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 cd8b2079e..14a8b43f7 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,12 +14,13 @@ 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" ) type Value interface { - Type() Type + Type() types.Type Yql() string castTo(dst interface{}) error @@ -29,7 +30,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 @@ -39,6 +40,7 @@ func ToYDB(v Value, a *allocator.Allocator) *Ydb.TypedValue { func BigEndianUint128(hi, lo uint64) (v [16]byte) { binary.BigEndian.PutUint64(v[0:8], hi) binary.BigEndian.PutUint64(v[8:16], lo) + return v } @@ -47,19 +49,20 @@ func FromYDB(t *Ydb.Type, v *Ydb.Value) Value { if err != nil { panic(err) } + 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 @@ -70,57 +73,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 @@ -130,29 +133,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)) @@ -160,117 +163,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)) } @@ -282,12 +301,17 @@ func (v boolValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *bool: *vv = bool(v) + return nil case *string: *vv = strconv.FormatBool(bool(v)) + 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, + )) } } @@ -295,8 +319,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 { @@ -320,18 +344,25 @@ func (v dateValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *time.Time: *vv = DateToTime(uint32(v)) + return nil case *uint64: *vv = uint64(v) + return nil case *int64: *vv = int64(v) + return nil case *int32: *vv = int32(v) + 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, + )) } } @@ -339,8 +370,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 { @@ -369,18 +400,25 @@ func (v datetimeValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *time.Time: *vv = DatetimeToTime(uint32(v)) + return nil case *uint64: *vv = uint64(v) + return nil case *int64: *vv = int64(v) + return nil case *uint32: *vv = uint32(v) + 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, + )) } } @@ -388,8 +426,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 { @@ -415,7 +453,7 @@ var _ DecimalValuer = (*decimalValue)(nil) type decimalValue struct { value [16]byte - innerType *DecimalType + innerType *types.Decimal } func (v *decimalValue) Value() [16]byte { @@ -423,11 +461,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 { @@ -437,7 +475,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 { @@ -446,18 +487,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 } @@ -478,16 +520,17 @@ func (v *decimalValue) toYDB(a *allocator.Allocator) *Ydb.Value { func DecimalValueFromBigInt(v *big.Int, precision, scale uint32) *decimalValue { b := decimal.BigIntToByte(v, precision, scale) + return DecimalValue(b, precision, scale) } func DecimalValue(v [16]byte, precision, scale uint32) *decimalValue { return &decimalValue{ value: v, - innerType: &DecimalType{ - Precision: precision, - Scale: scale, - }, + innerType: types.NewDecimal( + precision, + scale, + ), } } @@ -497,7 +540,7 @@ type ( V Value } dictValue struct { - t Type + t types.Type values []DictValueField } ) @@ -507,11 +550,15 @@ func (v *dictValue) DictValues() map[Value]Value { for i := range v.values { values[v.values[i].K] = v.values[i].V } + return values } 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 { @@ -527,10 +574,11 @@ func (v *dictValue) Yql() string { buffer.WriteString(v.values[i].V.Yql()) } buffer.WriteByte('}') + return buffer.String() } -func (v *dictValue) Type() Type { +func (v *dictValue) Type() types.Type { return v.t } @@ -547,7 +595,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 @@ -557,13 +605,14 @@ 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{ t: t, values: values, @@ -578,15 +627,21 @@ func (v *doubleValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatFloat(v.value, 'f', -1, 64) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatFloat(v.value, 'f', -1, 64)) + return nil case *float64: *vv = v.value + 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, + )) } } @@ -594,8 +649,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 { @@ -620,12 +675,17 @@ func (v dyNumberValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -633,8 +693,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 { @@ -659,18 +719,25 @@ func (v *floatValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatFloat(float64(v.value), 'f', -1, 32) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatFloat(float64(v.value), 'f', -1, 32)) + return nil case *float64: *vv = float64(v.value) + return nil case *float32: *vv = v.value + 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, + )) } } @@ -678,8 +745,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 { @@ -704,30 +771,41 @@ func (v int8Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *int64: *vv = int64(v) + return nil case *int32: *vv = int32(v) + return nil case *int16: *vv = int16(v) + return nil case *int8: *vv = int8(v) + return nil case *float64: *vv = float64(v) + return nil case *float32: *vv = float32(v) + 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, + )) } } @@ -735,8 +813,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 { @@ -759,27 +837,37 @@ func (v int16Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *int64: *vv = int64(v) + return nil case *int32: *vv = int32(v) + return nil case *int16: *vv = int16(v) + return nil case *float64: *vv = float64(v) + return nil case *float32: *vv = float32(v) + 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, + )) } } @@ -787,8 +875,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 { @@ -811,27 +899,37 @@ func (v int32Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *int64: *vv = int64(v) + return nil case *int: *vv = int(v) + return nil case *int32: *vv = int32(v) + return nil case *float64: *vv = float64(v) + return nil case *float32: *vv = float32(v) + 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, + )) } } @@ -839,8 +937,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 { @@ -863,18 +961,25 @@ func (v int64Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *int64: *vv = int64(v) + return nil case *float64: *vv = float64(v) + 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, + )) } } @@ -882,8 +987,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 { @@ -906,12 +1011,17 @@ func (v intervalValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *time.Duration: *vv = IntervalToDuration(int64(v)) + return nil case *int64: *vv = int64(v) + 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, + )) } } @@ -952,11 +1062,12 @@ func (v intervalValue) Yql() string { } buffer.WriteByte('"') buffer.WriteByte(')') + 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 { @@ -984,12 +1095,17 @@ func (v jsonValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -997,8 +1113,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 { @@ -1021,12 +1137,17 @@ func (v jsonDocumentValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -1034,8 +1155,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 { @@ -1053,7 +1174,7 @@ func JSONDocumentValue(v string) jsonDocumentValue { } type listValue struct { - t Type + t types.Type items []Value } @@ -1062,7 +1183,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 { @@ -1076,10 +1200,11 @@ func (v *listValue) Yql() string { buffer.WriteString(item.Yql()) } buffer.WriteByte(']') + return buffer.String() } -func (v *listValue) Type() Type { +func (v *listValue) Type() types.Type { return v.t } @@ -1091,33 +1216,70 @@ 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{ t: t, items: items, } } +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 { @@ -1131,10 +1293,11 @@ func (v *setValue) Yql() string { buffer.WriteString(item.Yql()) } buffer.WriteByte('}') + return buffer.String() } -func (v *setValue) Type() Type { +func (v *setValue) Type() types.Type { return v.t } @@ -1147,23 +1310,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{ @@ -1172,35 +1344,66 @@ 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 { if v.value == nil { return fmt.Sprintf("Nothing(%s)", v.Type().Yql()) } + return fmt.Sprintf("Just(%s)", v.value.Yql()) } -func (v *optionalValue) Type() Type { +func (v *optionalValue) Type() types.Type { return v.innerType } @@ -1212,17 +1415,18 @@ 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() } } + return vv } func OptionalValue(v Value) *optionalValue { return &optionalValue{ - innerType: Optional(v.Type()), + innerType: types.NewOptional(v.Type()), value: v, } } @@ -1233,7 +1437,7 @@ type ( V Value } structValue struct { - t Type + t types.Type fields []StructValueField } ) @@ -1243,11 +1447,15 @@ func (v *structValue) StructFields() map[string]Value { for i := range v.fields { fields[v.fields[i].Name] = v.fields[i].V } + return fields } 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 { @@ -1262,10 +1470,11 @@ func (v *structValue) Yql() string { buffer.WriteString(v.fields[i].V.Yql()) } buffer.WriteString("|>") + return buffer.String() } -func (v *structValue) Type() Type { +func (v *structValue) Type() types.Type { return v.t } @@ -1273,7 +1482,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 @@ -1283,12 +1492,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, } } @@ -1299,12 +1512,17 @@ func (v timestampValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *time.Time: *vv = TimestampToTime(uint64(v)) + return nil case *uint64: *vv = uint64(v) + 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, + )) } } @@ -1312,8 +1530,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 { @@ -1336,7 +1554,7 @@ func TimestampValueFromTime(t time.Time) timestampValue { } type tupleValue struct { - t Type + t types.Type items []Value } @@ -1348,7 +1566,11 @@ func (v *tupleValue) castTo(dst interface{}) error { if len(v.items) == 1 { 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 { @@ -1362,10 +1584,11 @@ func (v *tupleValue) Yql() string { buffer.WriteString(item.Yql()) } buffer.WriteByte(')') + return buffer.String() } -func (v *tupleValue) Type() Type { +func (v *tupleValue) Type() types.Type { return v.t } @@ -1377,19 +1600,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, } } @@ -1400,12 +1624,17 @@ func (v tzDateValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -1413,8 +1642,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 { @@ -1441,12 +1670,17 @@ func (v tzDatetimeValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -1454,8 +1688,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 { @@ -1482,12 +1716,17 @@ func (v tzTimestampValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -1495,8 +1734,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 { @@ -1523,39 +1762,53 @@ func (v uint8Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *uint64: *vv = uint64(v) + return nil case *int64: *vv = int64(v) + return nil case *uint32: *vv = uint32(v) + return nil case *int32: *vv = int32(v) + return nil case *uint16: *vv = uint16(v) + return nil case *int16: *vv = int16(v) + return nil case *uint8: *vv = uint8(v) + return nil case *float64: *vv = float64(v) + return nil case *float32: *vv = float32(v) + 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, + )) } } @@ -1563,8 +1816,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 { @@ -1587,33 +1840,45 @@ func (v uint16Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *uint64: *vv = uint64(v) + return nil case *int64: *vv = int64(v) + return nil case *uint32: *vv = uint32(v) + return nil case *int32: *vv = int32(v) + return nil case *uint16: *vv = uint16(v) + return nil case *float32: *vv = float32(v) + return nil case *float64: *vv = float64(v) + 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, + )) } } @@ -1621,8 +1886,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 { @@ -1645,24 +1910,33 @@ func (v uint32Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *uint64: *vv = uint64(v) + return nil case *int64: *vv = int64(v) + return nil case *uint32: *vv = uint32(v) + return nil case *float64: *vv = float64(v) + 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, + )) } } @@ -1670,8 +1944,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 { @@ -1694,15 +1968,21 @@ func (v uint64Value) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = strconv.FormatInt(int64(v), 10) + return nil case *[]byte: *vv = xstring.ToBytes(strconv.FormatInt(int64(v), 10)) + return nil case *uint64: *vv = uint64(v) + 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, + )) } } @@ -1710,8 +1990,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 { @@ -1734,12 +2014,17 @@ func (v textValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v) + return nil case *[]byte: *vv = xstring.ToBytes(string(v)) + 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, + )) } } @@ -1747,8 +2032,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 { @@ -1773,15 +2058,21 @@ func (v *uuidValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = string(v.value[:]) + return nil case *[]byte: *vv = v.value[:] + return nil case *[16]byte: *vv = v.value + 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, + )) } } @@ -1794,11 +2085,12 @@ func (v *uuidValue) Yql() string { buffer.WriteString(uuid.UUID(v.value).String()) buffer.WriteByte('"') buffer.WriteByte(')') + 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 { @@ -1821,15 +2113,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 } @@ -1850,18 +2142,19 @@ 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(',') buffer.WriteString(v.Type().Yql()) buffer.WriteByte(')') + return buffer.String() } -func (v *variantValue) Type() Type { +func (v *variantValue) Type() types.Type { return v.innerType } @@ -1877,10 +2170,11 @@ 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{ innerType: t, value: v, @@ -1888,25 +2182,28 @@ 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 }) } + return &variantValue{ innerType: t, value: v, @@ -1917,7 +2214,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 { @@ -1925,13 +2225,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 } @@ -1949,12 +2249,17 @@ func (v ysonValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = xstring.FromBytes(v) + return nil case *[]byte: *vv = v + 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, + )) } } @@ -1962,8 +2267,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 { @@ -1982,81 +2287,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: @@ -2064,53 +2369,57 @@ 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: + case *types.Decimal: return DecimalValue([16]byte{}, 22, 9) default: @@ -2124,12 +2433,17 @@ func (v bytesValue) castTo(dst interface{}) error { switch vv := dst.(type) { case *string: *vv = xstring.FromBytes(v) + return nil case *[]byte: *vv = v + 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, + )) } } @@ -2137,8 +2451,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 f2ea1fbe3..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) @@ -290,6 +296,7 @@ func TestValueYql(t *testing.T) { { value: DateValue(func() uint32 { v, _ := time.Parse("2006-01-02", "2022-06-17") + return uint32(v.Sub(time.Unix(0, 0)) / time.Hour / 24) }()), literal: `Date("2022-06-17")`, @@ -297,6 +304,7 @@ func TestValueYql(t *testing.T) { { value: DatetimeValue(func() uint32 { v, _ := time.Parse("2006-01-02 15:04:05", "2022-06-17 05:19:20") + return uint32(v.UTC().Sub(time.Unix(0, 0)).Seconds()) }()), literal: `Datetime("2022-06-17T05:19:20Z")`, @@ -317,6 +325,7 @@ func TestValueYql(t *testing.T) { value: TimestampValueFromTime(func() time.Time { tt, err := time.Parse(LayoutTimestamp, "1997-12-14T03:09:42.123456Z") require.NoError(t, err) + return tt.UTC() }()), literal: `Timestamp("1997-12-14T03:09:42.123456Z")`, @@ -326,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>)`, }, { @@ -387,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>)`, }, @@ -437,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")`, }, { @@ -479,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/parse.go b/internal/version/parse.go index f088defdb..e901ed031 100644 --- a/internal/version/parse.go +++ b/internal/version/parse.go @@ -33,6 +33,7 @@ func (lhs version) Less(rhs version) bool { if lhs.Patch > rhs.Patch { return false } + return lhs.Suffix < rhs.Suffix } @@ -46,6 +47,7 @@ func Lt(lhs, rhs string) bool { if err != nil { return false } + return v1.Less(v2) } @@ -62,6 +64,7 @@ func Gte(lhs, rhs string) bool { if v1.Less(v2) { return false } + return true } @@ -89,5 +92,6 @@ func parse(s string) (v version, err error) { return version{}, xerrors.WithStackTrace(err) } } + return v, nil } diff --git a/internal/version/version.go b/internal/version/version.go index 1a57d5071..367b11e50 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -2,8 +2,8 @@ package version const ( Major = "3" - Minor = "55" - Patch = "0" + Minor = "59" + Patch = "3" Prefix = "ydb-go-sdk" ) diff --git a/internal/wait/wait.go b/internal/wait/wait.go index cdf41e355..11a01dee6 100644 --- a/internal/wait/wait.go +++ b/internal/wait/wait.go @@ -22,6 +22,7 @@ func waitBackoff(ctx context.Context, b backoff.Backoff, i int) error { if err := ctx.Err(); err != nil { return xerrors.WithStackTrace(err) } + return nil } } @@ -33,6 +34,7 @@ func Wait(ctx context.Context, fastBackoff, slowBackoff backoff.Backoff, t backo if err := ctx.Err(); err != nil { return xerrors.WithStackTrace(err) } + return nil case backoff.TypeFast: if fastBackoff == nil { @@ -45,5 +47,6 @@ func Wait(ctx context.Context, fastBackoff, slowBackoff backoff.Backoff, t backo } b = slowBackoff } + return waitBackoff(ctx, b, i) } diff --git a/internal/xatomic/type.go b/internal/xatomic/type.go deleted file mode 100644 index 0ce078b19..000000000 --- a/internal/xatomic/type.go +++ /dev/null @@ -1,10 +0,0 @@ -package xatomic - -import "sync/atomic" - -type ( - Bool = atomic.Bool - Int64 = atomic.Int64 - Uint32 = atomic.Uint32 - Uint64 = atomic.Uint64 -) diff --git a/internal/xbytes/clone.go b/internal/xbytes/clone.go deleted file mode 100644 index 0b471be25..000000000 --- a/internal/xbytes/clone.go +++ /dev/null @@ -1,10 +0,0 @@ -package xbytes - -import "bytes" - -// Clone returns a copy of b[:len(b)]. -// The result may have additional unused capacity. -// Clone(nil) returns nil. -func Clone(b []byte) []byte { - return bytes.Clone(b) -} 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/context_with_cancel.go b/internal/xcontext/context_with_cancel.go index 7f983442c..1deeac4bb 100644 --- a/internal/xcontext/context_with_cancel.go +++ b/internal/xcontext/context_with_cancel.go @@ -11,6 +11,7 @@ func WithCancel(ctx context.Context) (context.Context, context.CancelFunc) { parentCtx: ctx, } childCtx.ctx, childCtx.ctxCancel = context.WithCancel(ctx) + return childCtx, childCtx.cancel } @@ -60,6 +61,7 @@ func (ctx *cancelCtx) cancel() { if err := ctx.parentCtx.Err(); err != nil { ctx.err = err + return } ctx.err = errAt(context.Canceled, 1) diff --git a/internal/xcontext/context_with_cancel_test.go b/internal/xcontext/context_with_cancel_test.go index 515d1a7a7..55251b671 100644 --- a/internal/xcontext/context_with_cancel_test.go +++ b/internal/xcontext/context_with_cancel_test.go @@ -46,6 +46,7 @@ func TestContextWithCancelError(t *testing.T) { childCtx, childCancel := WithCancel(parentCtx) parentCancel() childCancel() + return childCtx.Err() }(), str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithCancelError.func1(context_with_cancel_test.go:47)`", //nolint:lll @@ -54,24 +55,27 @@ func TestContextWithCancelError(t *testing.T) { err: func() error { ctx, cancel := WithCancel(context.Background()) cancel() + return ctx.Err() }(), - str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithCancelError.func2(context_with_cancel_test.go:56)`", //nolint:lll + str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithCancelError.func2(context_with_cancel_test.go:57)`", //nolint:lll }, { err: func() error { parentCtx, _ := WithTimeout(context.Background(), 0) childCtx, cancel := WithCancel(parentCtx) cancel() + return childCtx.Err() }(), - str: "'context deadline exceeded' from `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithCancelError.func3(context_with_cancel_test.go:63)`", //nolint:lll + str: "'context deadline exceeded' from `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithCancelError.func3(context_with_cancel_test.go:65)`", //nolint:lll }, { err: func() error { parentCtx, _ := context.WithTimeout(context.Background(), 0) //nolint:govet childCtx, cancel := WithCancel(parentCtx) cancel() + return childCtx.Err() }(), str: "context deadline exceeded", diff --git a/internal/xcontext/context_with_timeout.go b/internal/xcontext/context_with_timeout.go index 72d067044..5342798fe 100644 --- a/internal/xcontext/context_with_timeout.go +++ b/internal/xcontext/context_with_timeout.go @@ -14,6 +14,7 @@ func WithTimeout(ctx context.Context, t time.Duration) (context.Context, context from: stack.Record(1), } childCtx.ctx, childCtx.ctxCancel = context.WithTimeout(ctx, t) + return childCtx, childCtx.cancel } @@ -45,11 +46,13 @@ func (ctx *timeoutCtx) Err() error { if ctx.ctx.Err() == context.DeadlineExceeded && ctx.parentCtx.Err() == nil { //nolint:errorlint ctx.err = errFrom(context.DeadlineExceeded, ctx.from) + return ctx.err } if err := ctx.parentCtx.Err(); err != nil { ctx.err = err + return ctx.err } @@ -72,6 +75,7 @@ func (ctx *timeoutCtx) cancel() { if err := ctx.parentCtx.Err(); err != nil { ctx.err = err + return } ctx.err = errAt(context.Canceled, 1) diff --git a/internal/xcontext/context_with_timeout_test.go b/internal/xcontext/context_with_timeout_test.go index 6286776b3..d29cfa2a2 100644 --- a/internal/xcontext/context_with_timeout_test.go +++ b/internal/xcontext/context_with_timeout_test.go @@ -47,6 +47,7 @@ func TestContextWithTimeoutError(t *testing.T) { childCtx, childCancel := WithTimeout(parentCtx, time.Hour) parentCancel() childCancel() + return childCtx.Err() }(), str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func1(context_with_timeout_test.go:48)`", //nolint:lll @@ -55,39 +56,44 @@ func TestContextWithTimeoutError(t *testing.T) { err: func() error { ctx, cancel := WithTimeout(context.Background(), time.Hour) cancel() + return ctx.Err() }(), - str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func2(context_with_timeout_test.go:57)`", //nolint:lll + str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func2(context_with_timeout_test.go:58)`", //nolint:lll }, { err: func() error { parentCtx, _ := WithTimeout(context.Background(), 0) childCtx, _ := WithTimeout(parentCtx, 0) + return childCtx.Err() }(), - str: "'context deadline exceeded' from `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func3(context_with_timeout_test.go:64)`", //nolint:lll + str: "'context deadline exceeded' from `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func3(context_with_timeout_test.go:66)`", //nolint:lll }, { err: func() error { ctx, _ := WithTimeout(context.Background(), 0) + return ctx.Err() }(), - str: "'context deadline exceeded' from `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func4(context_with_timeout_test.go:72)`", //nolint:lll + str: "'context deadline exceeded' from `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func4(context_with_timeout_test.go:75)`", //nolint:lll }, { err: func() error { parentCtx, cancel := WithCancel(context.Background()) childCtx, _ := WithTimeout(parentCtx, 0) cancel() + return childCtx.Err() }(), - str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func5(context_with_timeout_test.go:81)`", //nolint:lll + str: "'context canceled' at `github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext.TestContextWithTimeoutError.func5(context_with_timeout_test.go:85)`", //nolint:lll }, { err: func() error { parentCtx, cancel := context.WithCancel(context.Background()) childCtx, _ := WithTimeout(parentCtx, 0) cancel() + return childCtx.Err() }(), str: "context canceled", diff --git a/internal/xcontext/idempotent.go b/internal/xcontext/idempotent.go index 377b9f995..9e8c7577b 100644 --- a/internal/xcontext/idempotent.go +++ b/internal/xcontext/idempotent.go @@ -15,5 +15,6 @@ func IsIdempotent(ctx context.Context) bool { if idempotent, ok := ctx.Value(ctxIdempotentKey{}).(bool); ok { return idempotent } + return false } diff --git a/internal/xcontext/local_dc.go b/internal/xcontext/local_dc.go index d549d57da..01f0602fd 100644 --- a/internal/xcontext/local_dc.go +++ b/internal/xcontext/local_dc.go @@ -12,5 +12,6 @@ func ExtractLocalDC(ctx context.Context) string { if val := ctx.Value(localDcKey{}); val != nil { return val.(string) } + return "" } diff --git a/internal/xcontext/retry_call.go b/internal/xcontext/retry_call.go index 501895db3..e7e0789d7 100644 --- a/internal/xcontext/retry_call.go +++ b/internal/xcontext/retry_call.go @@ -14,5 +14,6 @@ func IsNestedCall(ctx context.Context) bool { if _, has := ctx.Value(markRetryCallKey{}).(bool); has { return true } + return false } 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 68864a986..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,18 +19,24 @@ 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, TypeNonRetryable, // unknown errors are not retryable backoff.TypeNoBackoff, 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 f4870b685..066bb94eb 100644 --- a/internal/xerrors/issues.go +++ b/internal/xerrors/issues.go @@ -44,19 +44,20 @@ 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('}') } b.WriteByte(']') + return b.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 { @@ -64,17 +65,18 @@ func NewWithIssues(text string, issues ...error) error { err.issues = append(err.issues, issues[i]) } } + 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) @@ -89,24 +91,27 @@ func (e *errorWithIssues) Error() string { b.WriteString(issue.Error()) } b.WriteString("]") + 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 } } + 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 } } + return false } @@ -125,9 +130,10 @@ 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) } + return Issue{ Message: x.GetMessage(), Code: x.GetIssueCode(), diff --git a/internal/xerrors/join.go b/internal/xerrors/join.go index b5b29d683..80ec421c1 100644 --- a/internal/xerrors/join.go +++ b/internal/xerrors/join.go @@ -7,43 +7,50 @@ 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(',') } _, _ = fmt.Fprintf(b, "%q", err.Error()) } b.WriteByte(']') + 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 } } + 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 } } + return false } -func (errs joinError) Unwrap() []error { - return errs +func (e joinError) Unwrap() []error { + return e.errs } diff --git a/internal/xerrors/join_test.go b/internal/xerrors/join_test.go index a3db6a55c..a9ff7e297 100644 --- a/internal/xerrors/join_test.go +++ b/internal/xerrors/join_test.go @@ -24,8 +24,12 @@ func TestJoin(t *testing.T) { { err: Join(context.Canceled, context.DeadlineExceeded, Operation()), iss: []error{context.Canceled, context.DeadlineExceeded}, - ass: []interface{}{func() interface{} { var i isYdbError; return &i }()}, - s: "[\"context canceled\",\"context deadline exceeded\",\"operation/STATUS_CODE_UNSPECIFIED (code = 0)\"]", + ass: []interface{}{func() interface{} { + var i isYdbError + + return &i + }()}, + s: "[\"context canceled\",\"context deadline exceeded\",\"operation/STATUS_CODE_UNSPECIFIED (code = 0)\"]", }, } { t.Run("", func(t *testing.T) { diff --git a/internal/xerrors/operation.go b/internal/xerrors/operation.go index 36998f66e..7020e4f42 100644 --- a/internal/xerrors/operation.go +++ b/internal/xerrors/operation.go @@ -8,10 +8,11 @@ import ( "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Issue" "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" + "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring" ) -// operationError reports about operationStatus fail. +// operationError reports about operation fail. type operationError struct { code Ydb.StatusIds_StatusCode issues issues @@ -29,18 +30,13 @@ func (e *operationError) Name() string { return "operation/" + e.code.String() } -type operationStatus interface { - GetStatus() Ydb.StatusIds_StatusCode - GetIssues() []*Ydb_Issue.IssueMessage -} - type issuesOption []*Ydb_Issue.IssueMessage func (issues issuesOption) applyToOperationError(oe *operationError) { oe.issues = []*Ydb_Issue.IssueMessage(issues) } -// WithIssues is an option for construct operationStatus error with issues list +// WithIssues is an option for construct operation error with issues list // WithIssues must use as `Operation(WithIssues(issues))` func WithIssues(issues []*Ydb_Issue.IssueMessage) issuesOption { return issues @@ -52,7 +48,7 @@ func (code statusCodeOption) applyToOperationError(oe *operationError) { oe.code = Ydb.StatusIds_StatusCode(code) } -// WithStatusCode is an option for construct operationStatus error with reason code +// WithStatusCode is an option for construct operation error with reason code // WithStatusCode must use as `Operation(WithStatusCode(reason))` func WithStatusCode(code Ydb.StatusIds_StatusCode) statusCodeOption { return statusCodeOption(code) @@ -72,24 +68,25 @@ func (traceID traceIDOption) applyToOperationError(oe *operationError) { oe.traceID = string(traceID) } -// WithTraceID is an option for construct operationStatus error with traceID +// WithTraceID is an option for construct operation error with traceID func WithTraceID(traceID string) traceIDOption { return traceIDOption(traceID) } -type operationOption struct { - operationStatus -} +type operationOption = operationError -func (operation operationOption) applyToOperationError(oe *operationError) { - oe.code = operation.GetStatus() - oe.issues = operation.GetIssues() +func (e *operationOption) applyToOperationError(oe *operationError) { + oe.code = e.code + oe.issues = e.issues } -// FromOperation is an option for construct operationStatus error from operationStatus -// FromOperation must use as `Operation(FromOperation(operationStatus))` -func FromOperation(operation operationStatus) operationOption { - return operationOption{operation} +// FromOperation is an option for construct operation error from operation.Status +// FromOperation must use as `Operation(FromOperation(operation.Status))` +func FromOperation(operation operation.Status) *operationOption { + return &operationOption{ + code: operation.GetStatus(), + issues: operation.GetIssues(), + } } type oeOpt interface { @@ -105,6 +102,7 @@ func Operation(opts ...oeOpt) error { opt.applyToOperationError(oe) } } + return oe } @@ -126,6 +124,7 @@ func (e *operationError) Error() string { b.WriteString(e.issues.String()) } b.WriteString(")") + return b.String() } @@ -143,6 +142,7 @@ func IsOperationError(err error, codes ...Ydb.StatusIds_StatusCode) bool { return true } } + return false } @@ -154,6 +154,7 @@ func IsOperationErrorTransactionLocksInvalidated(err error) (isTLI bool) { isTLI = isTLI || (code == issueCodeTransactionLocksInvalidated) }) } + return isTLI } @@ -166,7 +167,9 @@ func (e *operationError) Type() Type { Ydb.StatusIds_BAD_SESSION, Ydb.StatusIds_SESSION_BUSY: return TypeRetryable - case Ydb.StatusIds_UNDETERMINED: + case + Ydb.StatusIds_UNDETERMINED, + Ydb.StatusIds_SESSION_EXPIRED: return TypeConditionallyRetryable default: return TypeUndefined @@ -189,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, @@ -206,5 +209,6 @@ func OperationError(err error) Error { if errors.As(err, &o) { return o } + return nil } diff --git a/internal/xerrors/retryable.go b/internal/xerrors/retryable.go index c2c2684ac..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,22 +66,24 @@ 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) } } + return re } @@ -91,5 +93,6 @@ func RetryableError(err error) Error { if errors.As(err, &e) { return e } + return nil } diff --git a/internal/xerrors/stacktrace.go b/internal/xerrors/stacktrace.go index 4f2e2e6f0..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 { @@ -38,6 +38,7 @@ func WithStackTrace(err error, opts ...withStackTraceOption) error { status: s, } } + return &stackError{ stackRecord: stack.Record(options.skipDepth + 1), err: err, diff --git a/internal/xerrors/transport.go b/internal/xerrors/transport.go index 0a42b8b68..b66f7735c 100644 --- a/internal/xerrors/transport.go +++ b/internal/xerrors/transport.go @@ -58,6 +58,7 @@ func (e *transportError) Error() string { b.WriteString(fmt.Sprintf(", traceID: %q", e.traceID)) } b.WriteString(")") + return b.String() } @@ -97,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, @@ -129,6 +130,7 @@ func IsTransportError(err error, codes ...grpcCodes.Code) bool { } } } + return false } @@ -139,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{ @@ -157,6 +159,7 @@ func Transport(err error, opts ...teOpt) error { opt.applyToTransportError(te) } } + return te } @@ -195,5 +198,6 @@ func TransportError(err error) Error { err: err, } } + return nil } diff --git a/internal/xerrors/xerrors.go b/internal/xerrors/xerrors.go index d0d4d0d44..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 { @@ -46,6 +46,7 @@ func ErrIf(cond bool, err error) error { if cond { return err } + return nil } @@ -56,6 +57,7 @@ func HideEOF(err error) error { if errors.Is(err, io.EOF) { return nil } + return err } @@ -73,9 +75,16 @@ func As(err error, targets ...interface{}) bool { return true } } + return false } +// IsErrorFromServer return true if err returned from server +// (opposite to raised internally in sdk) +func IsErrorFromServer(err error) bool { + return IsTransportError(err) || IsOperationError(err) +} + // Is is a improved proxy to errors.Is // This need to single import errors func Is(err error, targets ...error) bool { @@ -87,5 +96,10 @@ func Is(err error, targets ...error) bool { return true } } + return false } + +func IsContextError(err error) bool { + return Is(err, context.Canceled, context.DeadlineExceeded) +} diff --git a/internal/xerrors/ydb.go b/internal/xerrors/ydb.go index 1a5995948..3c168703c 100644 --- a/internal/xerrors/ydb.go +++ b/internal/xerrors/ydb.go @@ -10,6 +10,7 @@ type isYdbError interface { func IsYdb(err error) bool { var e isYdbError + return errors.As(err, &e) } diff --git a/internal/xrand/xrand.go b/internal/xrand/xrand.go index 8f2427c0d..2f8ed0021 100644 --- a/internal/xrand/xrand.go +++ b/internal/xrand/xrand.go @@ -35,11 +35,12 @@ 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) } } + return r } diff --git a/internal/xresolver/xresolver.go b/internal/xresolver/xresolver.go index 6ce395fa8..7eb2046a1 100644 --- a/internal/xresolver/xresolver.go +++ b/internal/xresolver/xresolver.go @@ -27,16 +27,18 @@ func (c *clientConn) Endpoint() string { if endpoint == "" { endpoint = c.target.URL.Opaque } + return strings.TrimPrefix(endpoint, "/") } 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) } + return }(), ) diff --git a/internal/xsql/badconn/badconn.go b/internal/xsql/badconn/badconn.go index 620c674a2..54f5deb8c 100644 --- a/internal/xsql/badconn/badconn.go +++ b/internal/xsql/badconn/badconn.go @@ -24,6 +24,7 @@ func (e Error) Is(err error) bool { if err == driver.ErrBadConn { //nolint:errorlint return true } + return xerrors.Is(e.err, err) } @@ -42,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 b5a52fd71..c5d670517 100644 --- a/internal/xsql/conn.go +++ b/internal/xsql/conn.go @@ -8,11 +8,12 @@ import ( "io" "path" "strings" + "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/xatomic" "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" @@ -75,8 +76,8 @@ type conn struct { beginTxFuncs map[QueryMode]beginTxFunc - closed xatomic.Bool - lastUsage xatomic.Int64 + closed atomic.Bool + lastUsage atomic.Int64 defaultQueryMode QueryMode defaultTxControl *table.TransactionControl @@ -135,12 +136,13 @@ 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) + return cc } @@ -153,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() { @@ -196,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(), ) ) @@ -206,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)) @@ -226,6 +228,7 @@ func (c *conn) execContext(ctx context.Context, query string, args []driver.Name if err = res.Err(); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return resultNoRows{}, nil case SchemeQueryMode: normalizedQuery, _, err := c.normalize(query) @@ -236,17 +239,15 @@ func (c *conn) execContext(ctx context.Context, query string, args []driver.Name if err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + 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)) } @@ -259,6 +260,7 @@ func (c *conn) execContext(ctx context.Context, query string, args []driver.Name if err = res.Err(); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return resultNoRows{}, nil default: return nil, fmt.Errorf("unsupported query mode '%s' for execute query", m) @@ -272,6 +274,7 @@ func (c *conn) ExecContext(ctx context.Context, query string, args []driver.Name if c.currentTx != nil { return c.currentTx.ExecContext(ctx, query, args) } + return c.execContext(ctx, query, args) } @@ -282,6 +285,7 @@ func (c *conn) QueryContext(ctx context.Context, query string, args []driver.Nam if c.currentTx != nil { return c.currentTx.QueryContext(ctx, query, args) } + return c.queryContext(ctx, query, args) } @@ -304,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(), ) ) @@ -314,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)) @@ -328,17 +332,18 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam if err = res.Err(); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return &rows{ conn: c, 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)) @@ -346,6 +351,7 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam if err = res.Err(); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return &rows{ conn: c, result: res, @@ -359,6 +365,7 @@ func (c *conn) queryContext(ctx context.Context, query string, args []driver.Nam if err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return &single{ values: []sql.NamedArg{ sql.Named("AST", exp.AST), @@ -366,17 +373,18 @@ 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)) } if err = res.Err(); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return &rows{ conn: c, result: res, @@ -387,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) }() @@ -397,6 +407,7 @@ func (c *conn) Ping(ctx context.Context) (finalErr error) { if err := c.session.KeepAlive(ctx); err != nil { return badconn.Map(xerrors.WithStackTrace(err)) } + return nil } @@ -405,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) @@ -413,12 +424,14 @@ 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)) } + return nil } + return badconn.Map(xerrors.WithStackTrace(errConnClosedEarly)) } @@ -430,11 +443,12 @@ 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]) } + return ii }()...) } @@ -445,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) }() @@ -453,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(), ), ) } @@ -469,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"), ), ), @@ -487,13 +503,14 @@ func (c *conn) BeginTx(ctx context.Context, txOptions driver.TxOptions) (_ drive func (c *conn) Version(_ context.Context) (_ string, _ error) { const version = "default" + return version, nil } 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() { @@ -506,6 +523,7 @@ func (c *conn) IsTableExists(ctx context.Context, tableName string) (tableExists if err != nil { return false, xerrors.WithStackTrace(err) } + return tableExists, nil } @@ -530,14 +548,17 @@ func (c *conn) IsColumnExists(ctx context.Context, tableName, columnName string) for i := range desc.Columns { if desc.Columns[i].Name == columnName { columnExists = true + break } } + return nil }, retry.WithIdempotent(true)) if err != nil { return false, xerrors.WithStackTrace(err) } + return columnExists, nil } @@ -562,11 +583,13 @@ func (c *conn) GetColumns(ctx context.Context, tableName string) (columns []stri for i := range desc.Columns { columns = append(columns, desc.Columns[i].Name) } + return nil }, retry.WithIdempotent(true)) if err != nil { return nil, xerrors.WithStackTrace(err) } + return columns, nil } @@ -599,14 +622,17 @@ func (c *conn) GetColumnType(ctx context.Context, tableName, columnName string) for i := range desc.Columns { if desc.Columns[i].Name == columnName { dataType = desc.Columns[i].Type.Yql() + break } } + return nil }, retry.WithIdempotent(true)) if err != nil { return "", xerrors.WithStackTrace(err) } + return dataType, nil } @@ -629,11 +655,13 @@ func (c *conn) GetPrimaryKeys(ctx context.Context, tableName string) (pkCols []s return err } pkCols = append(pkCols, desc.PrimaryKey...) + return nil }, retry.WithIdempotent(true)) if err != nil { return nil, xerrors.WithStackTrace(err) } + return pkCols, nil } @@ -665,9 +693,11 @@ func (c *conn) IsPrimaryKey(ctx context.Context, tableName, columnName string) ( for _, pkCol := range pkCols { if pkCol == columnName { ok = true + break } } + return ok, nil } @@ -684,6 +714,7 @@ func isSysDir(databaseName, dirAbsPath string) bool { return true } } + return false } @@ -697,6 +728,7 @@ func (c *conn) getTables(ctx context.Context, absPath string, recursive, exclude var d scheme.Directory err := retry.Retry(ctx, func(ctx context.Context) (err error) { d, err = c.connector.parent.Scheme().ListDirectory(ctx, absPath) + return err }, retry.WithIdempotent(true)) if err != nil { @@ -733,6 +765,7 @@ func (c *conn) GetTables(ctx context.Context, folder string, recursive, excludeS var e scheme.Entry err := retry.Retry(ctx, func(ctx context.Context) (err error) { e, err = c.connector.parent.Scheme().DescribePath(ctx, absPath) + return err }, retry.WithIdempotent(true)) if err != nil { @@ -751,6 +784,7 @@ func (c *conn) GetTables(ctx context.Context, folder string, recursive, excludeS for i := range tables { tables[i] = strings.TrimPrefix(tables[i], absPath+"/") } + return tables, nil default: return nil, xerrors.WithStackTrace( @@ -780,6 +814,7 @@ func (c *conn) GetIndexes(ctx context.Context, tableName string) (indexes []stri for i := range desc.Indexes { indexes = append(indexes, desc.Indexes[i].Name) } + return nil }, retry.WithIdempotent(true)) if err != nil { @@ -810,9 +845,11 @@ func (c *conn) GetIndexColumns(ctx context.Context, tableName, indexName string) for i := range desc.Indexes { if desc.Indexes[i].Name == indexName { columns = append(columns, desc.Indexes[i].IndexColumns...) + return nil } } + return xerrors.WithStackTrace(fmt.Errorf("index '%s' not found in table '%s'", indexName, tableName)) }, retry.WithIdempotent(true)) if err != nil { diff --git a/internal/xsql/connector.go b/internal/xsql/connector.go index 2812ac768..da43852e9 100644 --- a/internal/xsql/connector.go +++ b/internal/xsql/connector.go @@ -30,6 +30,7 @@ type defaultQueryModeConnectorOption QueryMode func (mode defaultQueryModeConnectorOption) Apply(c *Connector) error { c.defaultQueryMode = QueryMode(mode) + return nil } @@ -44,6 +45,7 @@ type queryBindConnectorOption struct { func (o queryBindConnectorOption) Apply(c *Connector) error { c.Bindings = bind.Sort(append(c.Bindings, o.Bind)) + return nil } @@ -54,6 +56,7 @@ type tablePathPrefixConnectorOption struct { func (o tablePathPrefixConnectorOption) Apply(c *Connector) error { c.Bindings = bind.Sort(append(c.Bindings, o.TablePathPrefix)) c.pathNormalizer = o.TablePathPrefix + return nil } @@ -75,6 +78,7 @@ type defaultTxControlOption struct { func (opt defaultTxControlOption) Apply(c *Connector) error { c.defaultTxControl = opt.txControl + return nil } @@ -86,6 +90,7 @@ type defaultDataQueryOptionsConnectorOption []options.ExecuteDataQueryOption func (opts defaultDataQueryOptionsConnectorOption) Apply(c *Connector) error { c.defaultDataQueryOpts = append(c.defaultDataQueryOpts, opts...) + return nil } @@ -97,6 +102,7 @@ type defaultScanQueryOptionsConnectorOption []options.ExecuteScanQueryOption func (opts defaultScanQueryOptionsConnectorOption) Apply(c *Connector) error { c.defaultScanQueryOpts = append(c.defaultScanQueryOpts, opts...) + return nil } @@ -111,6 +117,7 @@ type traceConnectorOption struct { func (option traceConnectorOption) Apply(c *Connector) error { c.trace = c.trace.Compose(option.t, option.opts...) + return nil } @@ -122,6 +129,7 @@ type disableServerBalancerConnectorOption struct{} func (d disableServerBalancerConnectorOption) Apply(c *Connector) error { c.disableServerBalancer = true + return nil } @@ -133,6 +141,7 @@ type idleThresholdConnectorOption time.Duration func (idleThreshold idleThresholdConnectorOption) Apply(c *Connector) error { c.idleThreshold = time.Duration(idleThreshold) + return nil } @@ -144,6 +153,7 @@ type onCloseConnectorOption func(connector *Connector) func (f onCloseConnectorOption) Apply(c *Connector) error { c.onClose = append(c.onClose, f) + return nil } @@ -157,6 +167,7 @@ type traceRetryConnectorOption struct { func (t traceRetryConnectorOption) Apply(c *Connector) error { c.traceRetry = t.t + return nil } @@ -168,6 +179,7 @@ type fakeTxConnectorOption QueryMode func (m fakeTxConnectorOption) Apply(c *Connector) error { c.fakeTxModes = append(c.fakeTxModes, QueryMode(m)) + return nil } @@ -203,6 +215,7 @@ func Open(parent ydbDriver, opts ...ConnectorOption) (_ *Connector, err error) { if c.idleThreshold > 0 { c.idleStopper = c.idleCloser() } + return c, nil } @@ -267,6 +280,7 @@ func (c *Connector) idleCloser() (idleStopper func()) { } } }() + return idleStopper } @@ -279,6 +293,7 @@ func (c *Connector) Close() (err error) { if c.idleStopper != nil { c.idleStopper() } + return nil } @@ -298,7 +313,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/context.go b/internal/xsql/context.go index 0b694e118..af1516de0 100644 --- a/internal/xsql/context.go +++ b/internal/xsql/context.go @@ -31,6 +31,7 @@ func queryModeFromContext(ctx context.Context, defaultQueryMode QueryMode) Query if m, ok := ctx.Value(ctxModeTypeKey{}).(QueryMode); ok { return m } + return defaultQueryMode } @@ -47,6 +48,7 @@ func txControl(ctx context.Context, defaultTxControl *table.TransactionControl) if txc, ok := ctx.Value(ctxTransactionControlKey{}).(*table.TransactionControl); ok { return txc } + return defaultTxControl } @@ -64,6 +66,7 @@ func (c *conn) scanQueryOptions(ctx context.Context) []options.ExecuteScanQueryO if opts, ok := ctx.Value(ctxScanQueryOptionsKey{}).([]options.ExecuteScanQueryOption); ok { return append(c.scanOpts, opts...) } + return c.scanOpts } @@ -81,6 +84,7 @@ func (c *conn) dataQueryOptions(ctx context.Context) []options.ExecuteDataQueryO if opts, ok := ctx.Value(ctxDataQueryOptionsKey{}).([]options.ExecuteDataQueryOption); ok { return append(c.dataOpts, opts...) } + return c.dataOpts } diff --git a/internal/xsql/dsn.go b/internal/xsql/dsn.go index 5fbff80ce..508308995 100644 --- a/internal/xsql/dsn.go +++ b/internal/xsql/dsn.go @@ -79,6 +79,7 @@ func Parse(dataSourceName string) (opts []config.Option, connectorOpts []Connect } connectorOpts = append(connectorOpts, binders...) } + return opts, connectorOpts, nil } @@ -92,5 +93,6 @@ func extractTablePathPrefixFromBinderName(binderName string) (string, error) { if len(ss) != 1 || len(ss[0]) != 2 || ss[0][1] == "" { return "", xerrors.WithStackTrace(fmt.Errorf("%w: %s", errWrongTablePathPrefix, binderName)) } + return ss[0][1], nil } diff --git a/internal/xsql/dsn_test.go b/internal/xsql/dsn_test.go index 309f9bf6a..d28c96ac3 100644 --- a/internal/xsql/dsn_test.go +++ b/internal/xsql/dsn_test.go @@ -13,10 +13,13 @@ 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) + } } } + return c } compareConfigs := func(t *testing.T, lhs, rhs *config.Config) { diff --git a/internal/xsql/errors.go b/internal/xsql/errors.go index 65c36486d..8dbcc7ce9 100644 --- a/internal/xsql/errors.go +++ b/internal/xsql/errors.go @@ -10,22 +10,23 @@ 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 default: return false diff --git a/internal/xsql/isolation/isolation.go b/internal/xsql/isolation/isolation.go index 2b1f026cf..e7f6a7b9d 100644 --- a/internal/xsql/isolation/isolation.go +++ b/internal/xsql/isolation/isolation.go @@ -25,6 +25,7 @@ func ToYDB(opts driver.TxOptions) (txcControl table.TxOption, err error) { return table.WithSnapshotReadOnly(), nil } } + return nil, xerrors.WithStackTrace(fmt.Errorf( "unsupported transaction options: %+v", opts, )) diff --git a/internal/xsql/mode.go b/internal/xsql/mode.go index 0070020c0..298bbd64e 100644 --- a/internal/xsql/mode.go +++ b/internal/xsql/mode.go @@ -36,6 +36,7 @@ func (t QueryMode) String() string { if s, ok := typeToString[t]; ok { return s } + return fmt.Sprintf("unknown_mode_%d", t) } @@ -43,5 +44,6 @@ func QueryModeFromString(s string) QueryMode { if t, ok := stringToType[s]; ok { return t } + return UnknownQueryMode } diff --git a/internal/xsql/rows.go b/internal/xsql/rows.go index b3adf339b..a9bb1572e 100644 --- a/internal/xsql/rows.go +++ b/internal/xsql/rows.go @@ -5,14 +5,15 @@ import ( "database/sql" "database/sql/driver" "io" + "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 ( @@ -22,7 +23,9 @@ var ( _ driver.RowsColumnTypeNullable = &rows{} _ driver.Rows = &single{} - _ types.Scanner = &valuer{} + _ scanner.Scanner = &valuer{} + + ignoreColumnPrefixName = "__discard_column_" ) type rows struct { @@ -43,8 +46,11 @@ func (r *rows) Columns() []string { }) cs := make([]string, 0, r.result.CurrentResultSet().ColumnCount()) r.result.CurrentResultSet().Columns(func(m options.Column) { - cs = append(cs, m.Name) + if !strings.HasPrefix(m.Name, ignoreColumnPrefixName) { + cs = append(cs, m.Name) + } }) + return cs } @@ -92,6 +98,7 @@ func (r *rows) NextResultSet() (finalErr error) { if err != nil { return badconn.Map(xerrors.WithStackTrace(err)) } + return nil } @@ -126,6 +133,7 @@ func (r *rows) Next(dst []driver.Value) error { if err = r.result.Err(); err != nil { return badconn.Map(xerrors.WithStackTrace(err)) } + return nil } @@ -142,6 +150,7 @@ func (r *single) Columns() (columns []string) { for i := range r.values { columns = append(columns, r.values[i].Name) } + return columns } @@ -157,5 +166,6 @@ func (r *single) Next(dst []driver.Value) error { dst[i] = r.values[i].Value } r.readAll = true + return nil } diff --git a/internal/xsql/stmt.go b/internal/xsql/stmt.go index 99d1d3131..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,10 +72,13 @@ 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) }() + return nil } diff --git a/internal/xsql/tx.go b/internal/xsql/tx.go index 5a135c0c9..d67813437 100644 --- a/internal/xsql/tx.go +++ b/internal/xsql/tx.go @@ -49,6 +49,7 @@ func (c *conn) beginTx(ctx context.Context, txOptions driver.TxOptions) (current txCtx: ctx, tx: transaction, } + return c.currentTx, nil } @@ -65,6 +66,7 @@ func (tx *tx) checkTxState() error { tx.ID(), tx.conn.ID(), ) } + return fmt.Errorf("broken conn state: tx=%s not related to conn=%q (conn have current tx=%q)", tx.conn.currentTx.ID(), tx.conn.ID(), tx.ID(), ) @@ -72,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() { @@ -88,12 +90,13 @@ func (tx *tx) Commit() (finalErr error) { if err != nil { return badconn.Map(xerrors.WithStackTrace(err)) } + return nil } 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() { @@ -109,6 +112,7 @@ func (tx *tx) Rollback() (finalErr error) { if err != nil { return badconn.Map(xerrors.WithStackTrace(err)) } + return err } @@ -116,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) @@ -128,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)) @@ -147,6 +151,7 @@ func (tx *tx) QueryContext(ctx context.Context, query string, args []driver.Name if err = res.Err(); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return &rows{ conn: tx.conn, result: res, @@ -157,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) @@ -169,28 +174,29 @@ 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)) } + return resultNoRows{}, nil } 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() { @@ -199,6 +205,7 @@ func (tx *tx) PrepareContext(ctx context.Context, query string) (_ driver.Stmt, if !tx.conn.isReady() { return nil, badconn.Map(xerrors.WithStackTrace(errNotReadyConn)) } + return &stmt{ conn: tx.conn, processor: tx, diff --git a/internal/xsql/tx_fake.go b/internal/xsql/tx_fake.go index 1488e00b6..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() { @@ -29,6 +28,7 @@ func (tx *txFake) PrepareContext(ctx context.Context, query string) (_ driver.St if !tx.conn.isReady() { return nil, badconn.Map(xerrors.WithStackTrace(errNotReadyConn)) } + return &stmt{ conn: tx.conn, processor: tx, @@ -58,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() { @@ -70,12 +70,13 @@ func (tx *txFake) Commit() (err error) { if !tx.conn.isReady() { return badconn.Map(xerrors.WithStackTrace(errNotReadyConn)) } + return nil } 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() { @@ -87,6 +88,7 @@ func (tx *txFake) Rollback() (err error) { if !tx.conn.isReady() { return badconn.Map(xerrors.WithStackTrace(errNotReadyConn)) } + return err } @@ -95,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) @@ -105,6 +107,7 @@ func (tx *txFake) QueryContext(ctx context.Context, query string, args []driver. if err != nil { return nil, xerrors.WithStackTrace(err) } + return rows, nil } @@ -113,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) @@ -123,5 +126,6 @@ func (tx *txFake) ExecContext(ctx context.Context, query string, args []driver.N if err != nil { return nil, xerrors.WithStackTrace(err) } + return result, nil } diff --git a/internal/xsql/unwrap.go b/internal/xsql/unwrap.go index 39755e50e..5f54b0252 100644 --- a/internal/xsql/unwrap.go +++ b/internal/xsql/unwrap.go @@ -15,17 +15,21 @@ func Unwrap[T *sql.DB | *sql.Conn](v T) (connector *Connector, err error) { if dw, ok := d.(*driverWrapper); ok { return dw.c, nil } + return nil, xerrors.WithStackTrace(fmt.Errorf("%T is not a *driverWrapper", d)) case *sql.Conn: if err = vv.Raw(func(driverConn interface{}) error { if cc, ok := driverConn.(*conn); ok { connector = cc.connector + return nil } + return xerrors.WithStackTrace(fmt.Errorf("%T is not a *conn", driverConn)) }); err != nil { return nil, badconn.Map(xerrors.WithStackTrace(err)) } + return connector, nil default: return nil, xerrors.WithStackTrace(fmt.Errorf("unknown type %T for Unwrap", vv)) diff --git a/internal/xsql/valuer.go b/internal/xsql/valuer.go index 750d52819..6171908b1 100644 --- a/internal/xsql/valuer.go +++ b/internal/xsql/valuer.go @@ -1,13 +1,16 @@ 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/xstring/convert.go b/internal/xstring/convert.go index 7881a16cb..eb7ee4b05 100644 --- a/internal/xstring/convert.go +++ b/internal/xstring/convert.go @@ -11,6 +11,7 @@ func FromBytes(b []byte) string { if len(b) == 0 { return "" } + return unsafe.String(&b[0], len(b)) } @@ -18,5 +19,6 @@ func ToBytes(s string) (b []byte) { if s == "" { return nil } + return unsafe.Slice(unsafe.StringData(s), len(s)) } diff --git a/internal/xsync/event_broadcast_test.go b/internal/xsync/event_broadcast_test.go index c52d57bf7..93a8860b7 100644 --- a/internal/xsync/event_broadcast_test.go +++ b/internal/xsync/event_broadcast_test.go @@ -2,13 +2,13 @@ package xsync import ( "runtime" + "sync/atomic" "testing" "time" "github.com/stretchr/testify/require" "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" ) @@ -24,12 +24,12 @@ func TestEventBroadcast(t *testing.T) { testDuration := time.Second / 100 b := &EventBroadcast{} - var events xatomic.Int64 + var events atomic.Int64 - var backgroundCounter xatomic.Int64 - firstWaiterStarted := xatomic.Bool{} + var backgroundCounter atomic.Int64 + firstWaiterStarted := atomic.Bool{} - stopSubscribe := xatomic.Bool{} + stopSubscribe := atomic.Bool{} subscribeStopped := make(empty.Chan) broadcastStopped := make(empty.Chan) @@ -51,7 +51,7 @@ func TestEventBroadcast(t *testing.T) { } }() - stopBroadcast := xatomic.Bool{} + stopBroadcast := atomic.Bool{} go func() { defer close(broadcastStopped) 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/context.go b/internal/xtest/context.go index 38cd0b73b..318cac0ba 100644 --- a/internal/xtest/context.go +++ b/internal/xtest/context.go @@ -17,6 +17,7 @@ func Context(t testing.TB) context.Context { pprof.SetGoroutineLabels(ctx) cancel() }) + return ctx } @@ -27,5 +28,6 @@ func ContextWithCommonTimeout(ctx context.Context, t testing.TB) context.Context ctx, ctxCancel := xcontext.WithTimeout(ctx, commonWaitTimeout) _ = ctxCancel // suppress linters, it is ok for leak for small amount of time: it will cancel by parent context + return ctx } diff --git a/internal/xtest/grpclogger.go b/internal/xtest/grpclogger.go index f42ddcc90..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 @@ -42,6 +42,7 @@ func (l GrpcLogger) UnaryClientInterceptor( req, reply, ) + return err } @@ -64,6 +65,7 @@ func (l GrpcLogger) StreamClientInterceptor( err, streamWrapper.streamID, ) + return stream, err } @@ -80,17 +82,20 @@ func newGrpcLoggerStream(stream grpc.ClientStream, t testing.TB) grpcLoggerStrea func (g grpcLoggerStream) CloseSend() error { err := g.ClientStream.CloseSend() g.t.Logf("CloseSend: %v (streamID: %v)", err, g.streamID) + return err } func (g grpcLoggerStream) SendMsg(m interface{}) error { err := g.ClientStream.SendMsg(m) g.t.Logf("SendMsg (streamID: %v) with err '%v':\n%v ", g.streamID, err, m) + return err } func (g grpcLoggerStream) RecvMsg(m interface{}) error { err := g.ClientStream.RecvMsg(m) g.t.Logf("RecvMsg (streamID: %v) with err '%v':\n%v ", g.streamID, err, m) + return err } 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/waiters.go b/internal/xtest/waiters.go index 1f09cb7f7..34389c1c0 100644 --- a/internal/xtest/waiters.go +++ b/internal/xtest/waiters.go @@ -64,6 +64,7 @@ func SpinWaitConditionWithTimeout(tb testing.TB, l sync.Locker, condWaitTimeout l.Lock() defer l.Unlock() } + return cond() } @@ -102,6 +103,7 @@ func SpinWaitProgressWithTimeout( SpinWaitConditionWithTimeout(tb, nil, timeout, func() bool { progressValue, finished := progress() + return finished || progressValue != currentValue }) } diff --git a/internal/xtest/ydb_grpc_mocks.go b/internal/xtest/ydb_grpc_mocks.go new file mode 100644 index 000000000..d3816feca --- /dev/null +++ b/internal/xtest/ydb_grpc_mocks.go @@ -0,0 +1,140 @@ +package xtest + +import ( + "context" + "fmt" + "net" + "reflect" + "strconv" + "time" + + "github.com/rekby/fixenv" + "github.com/rekby/fixenv/sf" + "github.com/ydb-platform/ydb-go-genproto/Ydb_Discovery_V1" + "github.com/ydb-platform/ydb-go-genproto/Ydb_Topic_V1" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Discovery" + "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/anypb" +) + +func GrpcMockTopicConnString(e fixenv.Env, topicServiceImpl Ydb_Topic_V1.TopicServiceServer) string { + v := reflect.ValueOf(topicServiceImpl) + addr := v.Pointer() + + var f fixenv.GenericFixtureFunction[string] = func() (*fixenv.GenericResult[string], error) { + listener := sf.LocalTCPListenerNamed(e, fmt.Sprintf("ydb-grpc-mock-topic-%v", addr)) + connString := fmt.Sprintf("grpc://%s/local", listener.Addr()) + + mock, err := newGrpcMock(listener, topicServiceImpl) + if err != nil { + return nil, fmt.Errorf("failed to create grpc mock: %w", err) + } + + clean := func() { + _ = mock.Close() + } + + return fixenv.NewGenericResultWithCleanup(connString, clean), nil + } + + return fixenv.CacheResult(e, f, fixenv.CacheOptions{CacheKey: addr}) +} + +type grpcMock struct { + listener net.Listener + grpcServer *grpc.Server + stopChan chan error +} + +func (m *grpcMock) Close() error { + m.grpcServer.Stop() + + return m.listener.Close() +} + +func newGrpcMock(listener net.Listener, topicServiceImpl Ydb_Topic_V1.TopicServiceServer) (*grpcMock, error) { + res := &grpcMock{ + listener: listener, + grpcServer: grpc.NewServer(), + stopChan: make(chan error, 1), + } + + host, portS, err := net.SplitHostPort(listener.Addr().String()) + if err != nil { + return nil, fmt.Errorf("failed to split host port addresses: %w", err) + } + + port, err := strconv.ParseUint(portS, 10, 32) + if err != nil { + return nil, fmt.Errorf("failed convert port to int: %w", err) + } + discoveryService := newMockDiscoveryService(host, uint32(port)) + + Ydb_Discovery_V1.RegisterDiscoveryServiceServer(res.grpcServer, discoveryService) + Ydb_Topic_V1.RegisterTopicServiceServer(res.grpcServer, topicServiceImpl) + + go func() { + res.stopChan <- res.grpcServer.Serve(res.listener) + }() + + select { + case <-res.stopChan: + return nil, err + case <-time.After(time.Millisecond): + return res, nil + } +} + +type mockDiscoveryService struct { + Ydb_Discovery_V1.UnimplementedDiscoveryServiceServer + host string + port uint32 +} + +func newMockDiscoveryService(host string, port uint32) *mockDiscoveryService { + return &mockDiscoveryService{ + host: host, + port: port, + } +} + +func (m mockDiscoveryService) ListEndpoints( + ctx context.Context, + request *Ydb_Discovery.ListEndpointsRequest, +) (*Ydb_Discovery.ListEndpointsResponse, error) { + res := &Ydb_Discovery.ListEndpointsResult{ + Endpoints: []*Ydb_Discovery.EndpointInfo{ + { + Address: m.host, + Port: m.port, + LoadFactor: 0, + Ssl: false, + Service: nil, + Location: "", + NodeId: 1, + IpV4: []string{"127.0.0.1"}, + }, + }, + SelfLocation: "", + } + resp := &Ydb_Discovery.ListEndpointsResponse{ + Operation: &Ydb_Operations.Operation{ + Id: "test-list-operation", + Ready: true, + Status: Ydb.StatusIds_SUCCESS, + Result: &anypb.Any{}, + }, + } + err := resp.GetOperation().GetResult().MarshalFrom(res) + + return resp, err +} + +func (m mockDiscoveryService) WhoAmI( + ctx context.Context, + request *Ydb_Discovery.WhoAmIRequest, +) (*Ydb_Discovery.WhoAmIResponse, error) { + panic("unimplemented") +} diff --git a/log/context.go b/log/context.go index f376f47ce..81231df01 100644 --- a/log/context.go +++ b/log/context.go @@ -13,6 +13,7 @@ func WithLevel(ctx context.Context, lvl Level) context.Context { func LevelFromContext(ctx context.Context) Level { v, _ := ctx.Value(ctxLevelKey{}).(Level) + return v } @@ -25,6 +26,7 @@ func NamesFromContext(ctx context.Context) []string { if v == nil { return []string{} } + return v } diff --git a/log/discovery.go b/log/discovery.go index f9b7c60d5..452c61876 100644 --- a/log/discovery.go +++ b/log/discovery.go @@ -11,7 +11,7 @@ func Discovery(l Logger, d trace.Detailer, opts ...Option) (t trace.Discovery) { return internalDiscovery(wrapLogger(l, opts...), d) } -func internalDiscovery(l *wrapper, d trace.Detailer) (t trace.Discovery) { +func internalDiscovery(l Logger, d trace.Detailer) (t trace.Discovery) { t.OnDiscover = func(info trace.DiscoveryDiscoverStartInfo) func(trace.DiscoveryDiscoverDoneInfo) { if d.Details()&trace.DiscoveryEvents == 0 { return nil @@ -22,6 +22,7 @@ func internalDiscovery(l *wrapper, d trace.Detailer) (t trace.Discovery) { String("database", info.Database), ) start := time.Now() + return func(info trace.DiscoveryDiscoverDoneInfo) { if info.Error == nil { l.Log(WithLevel(ctx, INFO), "done", @@ -44,6 +45,7 @@ func internalDiscovery(l *wrapper, d trace.Detailer) (t trace.Discovery) { ctx := with(*info.Context, TRACE, "ydb", "discovery", "whoAmI") l.Log(ctx, "start") start := time.Now() + return func(info trace.DiscoveryWhoAmIDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -60,5 +62,6 @@ func internalDiscovery(l *wrapper, d trace.Detailer) (t trace.Discovery) { } } } + return t } diff --git a/log/driver.go b/log/driver.go index 8292f31bc..fb9c8b1a1 100644 --- a/log/driver.go +++ b/log/driver.go @@ -13,257 +13,185 @@ func Driver(l Logger, d trace.Detailer, opts ...Option) (t trace.Driver) { return internalDriver(wrapLogger(l, opts...), d) } -func internalDriver(l *wrapper, 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() - 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(), - ) + 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(), + ) + } } - } - } - 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() - 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(), - ) + }, + OnInit: func(info trace.DriverInitStartInfo) func(trace.DriverInitDoneInfo) { + if d.Details()&trace.DriverEvents == 0 { + return nil } - } - } - 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() - 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(), - ) + 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.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.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() - return func(info trace.DriverConnStateChangeDoneInfo) { - l.Log(ctx, "done", + }, + 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.DriverCloseDoneInfo) { + if info.Error == nil { + l.Log(ctx, "done", + latencyField(start), + ) + } else { + l.Log(WithLevel(ctx, WARN), "failed", + Error(info.Error), + latencyField(start), + versionField(), + ) + } + } + }, + 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.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() - return func(info trace.DriverConnParkDoneInfo) { - if info.Error == nil { + start := time.Now() + + 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() - 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(), - ) + 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(), + ) + } } - } - } - 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 } - return func(info trace.DriverConnNewStreamDoneInfo) { + 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.DriverConnInvokeDoneInfo) { if info.Error == nil { l.Log(ctx, "done", Stringer("endpoint", endpoint), @@ -282,182 +210,293 @@ func internalDriver(l *wrapper, d trace.Detailer) (t trace.Driver) { //nolint:go ) } } - } - } - 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() - return func(info trace.DriverConnAllowDoneInfo) { - l.Log(ctx, "done", + 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.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() - 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), + start := time.Now() + + 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() - return func(info trace.DriverBalancerCloseDoneInfo) { - if info.Error == nil { + start := time.Now() + + 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() - 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), + }, + 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.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() - 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), + }, + 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.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() - return func(info trace.DriverGetCredentialsDoneInfo) { - if info.Error == nil { + start := time.Now() + + 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 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(), + ) + } + } + }, } - return t } diff --git a/log/field.go b/log/field.go index 605064987..af6f65598 100644 --- a/log/field.go +++ b/log/field.go @@ -51,30 +51,35 @@ func (f Field) Key() string { // StringValue is a value getter for fields with StringType type func (f Field) StringValue() string { f.checkType(StringType) + return f.vstr } // IntValue is a value getter for fields with IntType type func (f Field) IntValue() int { f.checkType(IntType) + return int(f.vint) } // Int64Value is a value getter for fields with Int64Type type func (f Field) Int64Value() int64 { f.checkType(Int64Type) + return f.vint } // BoolValue is a value getter for fields with BoolType type func (f Field) BoolValue() bool { f.checkType(BoolType) + return f.vint != 0 } // DurationValue is a value getter for fields with DurationType type func (f Field) DurationValue() time.Duration { f.checkType(DurationType) + return time.Nanosecond * time.Duration(f.vint) } @@ -84,6 +89,7 @@ func (f Field) StringsValue() []string { if f.vany == nil { return nil } + return f.vany.([]string) } @@ -93,6 +99,7 @@ func (f Field) ErrorValue() error { if f.vany == nil { return nil } + return f.vany.(error) } @@ -128,6 +135,7 @@ func (f Field) Stringer() fmt.Stringer { if f.vany == nil { return nil } + return f.vany.(fmt.Stringer) } @@ -156,6 +164,7 @@ func (f Field) String() string { if f.vany == nil || f.vany.(error) == nil { return "" } + return f.ErrorValue().Error() case AnyType: if f.vany == nil { @@ -165,8 +174,10 @@ func (f Field) String() string { if v.IsNil() { return nilPtr } + return v.Type().String() + "(" + fmt.Sprint(v.Elem()) + ")" } + return fmt.Sprint(f.vany) case StringerType: return f.Stringer().String() @@ -209,6 +220,7 @@ func Bool(key string, value bool) Field { } else { vint = 0 } + return Field{ ftype: BoolType, key: key, @@ -263,6 +275,7 @@ func Stringer(key string, value fmt.Stringer) Field { if value == nil { return Any(key, nil) } + return Field{ ftype: StringerType, key: key, @@ -327,6 +340,7 @@ func (ft FieldType) String() (typeName string) { default: panic("not implemented") } + return typeName } @@ -353,6 +367,7 @@ func (ee endpoints) String() string { b.WriteString(e.String()) } b.WriteByte(']') + return b.String() } @@ -363,6 +378,7 @@ func (m metadata) String() string { if err != nil { return fmt.Sprintf("error:%s", err) } + return xstring.FromBytes(b) } @@ -370,5 +386,6 @@ func appendFieldByCondition(condition bool, ifTrueField Field, fields ...Field) if condition { fields = append(fields, ifTrueField) } + return fields } diff --git a/log/field_test.go b/log/field_test.go index 73b3f8e49..af2dfb217 100644 --- a/log/field_test.go +++ b/log/field_test.go @@ -48,11 +48,13 @@ func TestField_String(t *testing.T) { // Known fieldType, but String() panics with it. if tt.panic { require.Panics(t, func() { _ = tt.f.String() }) + return } // Unknown fieldType, maybe a new one has been added if tt.fail { t.Fail() + return } require.Equal(t, tt.want, tt.f.String()) diff --git a/log/logger.go b/log/logger.go index c5391180a..8caf7229a 100644 --- a/log/logger.go +++ b/log/logger.go @@ -33,17 +33,20 @@ 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 } type defaultLogger struct { coloring bool - clock clockwork.Clock logQuery bool minLevel Level + clock clockwork.Clock w io.Writer } @@ -77,6 +80,7 @@ func (l *defaultLogger) format(namespace []string, msg string, logLevel Level) s if l.coloring { b.WriteString(colorReset) } + return b.String() } @@ -102,11 +106,12 @@ 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) } } + return ll } @@ -125,6 +130,7 @@ func (l *defaultLogger) appendFields(msg string, fields ...Field) string { fmt.Fprintf(b, `%q:%q`, fields[i].Key(), fields[i].String()) } b.WriteByte('}') + return b.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 4f0c582b6..df772bdb3 100644 --- a/log/retry.go +++ b/log/retry.go @@ -13,14 +13,8 @@ func Retry(l Logger, d trace.Detailer, opts ...Option) (t trace.Retry) { return internalRetry(wrapLogger(l, opts...), d) } -func internalRetry(l *wrapper, d trace.Detailer) (t trace.Retry) { - t.OnRetry = func( - info trace.RetryLoopStartInfo, - ) func( - trace.RetryLoopIntermediateInfo, - ) func( - trace.RetryLoopDoneInfo, - ) { +func internalRetry(l Logger, d trace.Detailer) (t trace.Retry) { + t.OnRetry = func(info trace.RetryLoopStartInfo) func(trace.RetryLoopDoneInfo) { if d.Details()&trace.RetryEvents == 0 { return nil } @@ -32,11 +26,13 @@ func internalRetry(l *wrapper, d trace.Detailer) (t trace.Retry) { Bool("idempotent", idempotent), ) 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 @@ -44,42 +40,19 @@ func internalRetry(l *wrapper, 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(), - ) - } - } } } + return t } diff --git a/log/scripting.go b/log/scripting.go index 6d45d60a1..4af256cb1 100644 --- a/log/scripting.go +++ b/log/scripting.go @@ -19,6 +19,7 @@ func internalScripting(l *wrapper, d trace.Detailer) (t trace.Scripting) { ctx := with(*info.Context, TRACE, "ydb", "scripting", "execute") l.Log(ctx, "start") start := time.Now() + return func(info trace.ScriptingExecuteDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -42,6 +43,7 @@ func internalScripting(l *wrapper, d trace.Detailer) (t trace.Scripting) { ctx := with(*info.Context, TRACE, "ydb", "scripting", "explain") l.Log(ctx, "start") start := time.Now() + return func(info trace.ScriptingExplainDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -75,6 +77,7 @@ func internalScripting(l *wrapper, d trace.Detailer) (t trace.Scripting) { )..., ) start := time.Now() + return func( info trace.ScriptingStreamExecuteIntermediateInfo, ) func( @@ -88,6 +91,7 @@ func internalScripting(l *wrapper, d trace.Detailer) (t trace.Scripting) { versionField(), ) } + return func(info trace.ScriptingStreamExecuteDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -116,6 +120,7 @@ func internalScripting(l *wrapper, d trace.Detailer) (t trace.Scripting) { ctx := with(*info.Context, TRACE, "ydb", "scripting", "close") l.Log(ctx, "start") start := time.Now() + return func(info trace.ScriptingCloseDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -130,5 +135,6 @@ func internalScripting(l *wrapper, d trace.Detailer) (t trace.Scripting) { } } } + return t } diff --git a/log/sql.go b/log/sql.go index a89994b37..5ef2a56f3 100644 --- a/log/sql.go +++ b/log/sql.go @@ -25,6 +25,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(*info.Context, TRACE, "ydb", "database", "sql", "connector", "connect") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLConnectorConnectDoneInfo) { if info.Error == nil { l.Log(WithLevel(ctx, DEBUG), "connected", @@ -49,6 +50,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(*info.Context, TRACE, "ydb", "database", "sql", "conn", "ping") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLConnPingDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -70,6 +72,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(context.Background(), TRACE, "ydb", "database", "sql", "conn", "close") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLConnCloseDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -91,6 +94,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(*info.Context, TRACE, "ydb", "database", "sql", "conn", "begin", "tx") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLConnBeginDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -117,6 +121,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ) query := info.Query start := time.Now() + return func(info trace.DatabaseSQLConnPrepareDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -147,6 +152,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { query := info.Query idempotent := info.Idempotent start := time.Now() + return func(info trace.DatabaseSQLConnExecDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -159,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(), @@ -181,6 +187,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { query := info.Query idempotent := info.Idempotent start := time.Now() + return func(info trace.DatabaseSQLConnQueryDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -193,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(), @@ -209,6 +216,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(*info.Context, TRACE, "ydb", "database", "sql", "tx", "commit") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLTxCommitDoneInfo) { if info.Error == nil { l.Log(ctx, "committed", @@ -230,6 +238,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(*info.Context, TRACE, "ydb", "database", "sql", "tx", "rollback") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLTxRollbackDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -251,6 +260,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ctx := with(context.Background(), TRACE, "ydb", "database", "sql", "stmt", "close") l.Log(ctx, "start") start := time.Now() + return func(info trace.DatabaseSQLStmtCloseDoneInfo) { if info.Error == nil { l.Log(ctx, "closed", @@ -277,6 +287,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ) query := info.Query start := time.Now() + return func(info trace.DatabaseSQLStmtExecDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -307,6 +318,7 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { ) query := info.Query start := time.Now() + return func(info trace.DatabaseSQLStmtQueryDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -324,5 +336,6 @@ func internalDatabaseSQL(l *wrapper, d trace.Detailer) (t trace.DatabaseSQL) { } } } + return t } diff --git a/log/table.go b/log/table.go index 0570dcbf1..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, ) { @@ -34,63 +32,37 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("label", label), ) 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, ) { @@ -105,15 +77,17 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("label", label), ) 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 } @@ -122,46 +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, ) { @@ -171,35 +117,23 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { ctx := with(*info.Context, TRACE, "ydb", "table", "create", "session") 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) { @@ -209,6 +143,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { ctx := with(*info.Context, TRACE, "ydb", "table", "session", "new") l.Log(ctx, "start") start := time.Now() + return func(info trace.TableSessionNewDoneInfo) { if info.Error == nil { if info.Session != nil { @@ -242,6 +177,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("status", info.Session.Status()), ) start := time.Now() + return func(info trace.TableSessionDeleteDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -271,6 +207,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("status", session.Status()), ) start := time.Now() + return func(info trace.TableKeepAliveDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -308,6 +245,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { )..., ) start := time.Now() + return func(info trace.TablePrepareDataQueryDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -354,6 +292,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { )..., ) start := time.Now() + return func(info trace.TableExecuteDataQueryDoneInfo) { if info.Error == nil { tx := info.Tx @@ -385,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, ) { @@ -404,49 +341,34 @@ 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, ) { @@ -460,42 +382,29 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("status", session.Status()), ) 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 @@ -507,7 +416,8 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("status", session.Status()), ) start := time.Now() - return func(info trace.TableSessionTransactionBeginDoneInfo) { + + return func(info trace.TableTxBeginDoneInfo) { if info.Error == nil { l.Log(ctx, "done", latencyField(start), @@ -526,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 } @@ -543,7 +449,8 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("tx", info.Tx.ID()), ) start := time.Now() - return func(info trace.TableSessionTransactionCommitDoneInfo) { + + return func(info trace.TableTxCommitDoneInfo) { if info.Error == nil { l.Log(ctx, "done", latencyField(start), @@ -563,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 @@ -580,7 +487,8 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("tx", tx.ID()), ) start := time.Now() - return func(info trace.TableSessionTransactionRollbackDoneInfo) { + + return func(info trace.TableTxRollbackDoneInfo) { if info.Error == nil { l.Log(ctx, "done", latencyField(start), @@ -607,6 +515,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { ctx := with(*info.Context, TRACE, "ydb", "table", "init") l.Log(ctx, "start") start := time.Now() + return func(info trace.TableInitDoneInfo) { l.Log(WithLevel(ctx, INFO), "done", latencyField(start), @@ -621,6 +530,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { ctx := with(*info.Context, TRACE, "ydb", "table", "close") l.Log(ctx, "start") start := time.Now() + return func(info trace.TableCloseDoneInfo) { if info.Error == nil { l.Log(WithLevel(ctx, INFO), "done", @@ -676,6 +586,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { String("status", session.Status()), ) start := time.Now() + return func(info trace.TablePoolPutDoneInfo) { if info.Error == nil { l.Log(ctx, "done", @@ -701,6 +612,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { ctx := with(*info.Context, TRACE, "ydb", "table", "pool", "get") l.Log(ctx, "start") start := time.Now() + return func(info trace.TablePoolGetDoneInfo) { if info.Error == nil { session := info.Session @@ -727,6 +639,7 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { ctx := with(*info.Context, TRACE, "ydb", "table", "pool", "wait") l.Log(ctx, "start") start := time.Now() + return func(info trace.TablePoolWaitDoneInfo) { fields := []Field{ latencyField(start), @@ -745,5 +658,6 @@ func internalTable(l *wrapper, d trace.Detailer) (t trace.Table) { } } } + return t } diff --git a/log/topic.go b/log/topic.go index 769d081f0..87d645d9b 100644 --- a/log/topic.go +++ b/log/topic.go @@ -12,7 +12,7 @@ func Topic(l Logger, d trace.Detailer, opts ...Option) (t trace.Topic) { return internalTopic(wrapLogger(l, opts...), d) } -func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocyclo +func internalTopic(l Logger, d trace.Detailer) (t trace.Topic) { //nolint:gocyclo t.OnReaderReconnect = func( info trace.TopicReaderReconnectStartInfo, ) func(doneInfo trace.TopicReaderReconnectDoneInfo) { @@ -22,6 +22,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy ctx := with(context.Background(), TRACE, "ydb", "topic", "reader", "reconnect") start := time.Now() l.Log(ctx, "start") + return func(doneInfo trace.TopicReaderReconnectDoneInfo) { l.Log(WithLevel(ctx, INFO), "reconnected", NamedError("reason", info.Reason), @@ -53,6 +54,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int64("partition_id", info.PartitionID), Int64("partition_session_id", info.PartitionSessionID), ) + return func(doneInfo trace.TopicReaderPartitionReadStartResponseDoneInfo) { fields := []Field{ String("topic", info.Topic), @@ -98,6 +100,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int64("partition_session_id", info.PartitionSessionID), Int64("committed_offset", info.CommittedOffset), Bool("graceful", info.Graceful)) + return func(doneInfo trace.TopicReaderPartitionReadStopResponseDoneInfo) { fields := []Field{ String("reader_connection_id", info.ReaderConnectionID), @@ -133,6 +136,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int64("commit_start_offset", info.StartOffset), Int64("commit_end_offset", info.EndOffset), ) + return func(doneInfo trace.TopicReaderCommitDoneInfo) { fields := []Field{ String("topic", info.Topic), @@ -173,6 +177,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int64("commit_end_offset", commitInfo[i].EndOffset), ) } + return func(doneInfo trace.TopicReaderSendCommitMessageDoneInfo) { for i := range commitInfo { fields := []Field{ @@ -219,6 +224,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy String("reader_connection_id", info.ReaderConnectionID), NamedError("close_reason", info.CloseReason), ) + return func(doneInfo trace.TopicReaderCloseDoneInfo) { fields := []Field{ String("reader_connection_id", info.ReaderConnectionID), @@ -248,6 +254,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy String("consumer", info.InitRequestInfo.GetConsumer()), Strings("topics", info.InitRequestInfo.GetTopics()), ) + return func(doneInfo trace.TopicReaderInitDoneInfo) { fields := []Field{ String("pre_init_reader_connection_id", info.PreInitReaderConnectionID), @@ -291,6 +298,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy l.Log(ctx, "token updating...", String("reader_connection_id", info.ReaderConnectionID), ) + return func( updateTokenInfo trace.OnReadUpdateTokenMiddleTokenReceivedInfo, ) func(doneInfo trace.OnReadStreamUpdateTokenDoneInfo) { @@ -309,6 +317,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy versionField(), ) } + return func(doneInfo trace.OnReadStreamUpdateTokenDoneInfo) { if doneInfo.Error == nil { l.Log(ctx, "token updated on stream", @@ -356,6 +365,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int("batches_count", batchesCount), Int("messages_count", messagesCount), ) + return func(doneInfo trace.TopicReaderReceiveDataResponseDoneInfo) { if doneInfo.Error == nil { l.Log(ctx, "data response received and processed", @@ -395,6 +405,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int("max_count", info.MaxCount), Int("local_capacity_before", info.FreeBufferCapacity), ) + return func(doneInfo trace.TopicReaderReadMessagesDoneInfo) { if doneInfo.Error == nil { l.Log(ctx, "read messages returned", @@ -443,6 +454,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy String("writer_instance_id", info.WriterInstanceID), Int("attempt", info.Attempt), ) + return func(doneInfo trace.TopicWriterReconnectDoneInfo) { if doneInfo.Error == nil { l.Log(WithLevel(ctx, DEBUG), "connect to topic writer stream completed", @@ -510,6 +522,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy String("writer_instance_id", info.WriterInstanceID), NamedError("reason", info.Reason), ) + return func(doneInfo trace.TopicWriterCloseDoneInfo) { if doneInfo.Error == nil { l.Log(WithLevel(ctx, DEBUG), "close topic writer completed", @@ -544,6 +557,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int("messages_count", info.MessagesCount), Int64("first_seqno", info.FirstSeqNo), ) + return func(doneInfo trace.TopicWriterCompressMessagesDoneInfo) { if doneInfo.Error == nil { l.Log(ctx, "compress message completed", @@ -585,6 +599,7 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy Int("messages_count", info.MessagesCount), Int64("first_seqno", info.FirstSeqNo), ) + return func(doneInfo trace.TopicWriterSendMessagesDoneInfo) { if doneInfo.Error == nil { l.Log(ctx, "send messages completed", @@ -619,5 +634,6 @@ func internalTopic(l *wrapper, d trace.Detailer) (t trace.Topic) { //nolint:gocy String("session_id", info.SessionID), ) } + return t } diff --git a/meta/consumed_units.go b/meta/consumed_units.go index 48f89b7b2..ea4617962 100644 --- a/meta/consumed_units.go +++ b/meta/consumed_units.go @@ -20,5 +20,6 @@ func ConsumedUnits(md metadata.MD) (consumedUnits uint64) { } } } + return consumedUnits } 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/meta/example_test.go b/meta/example_test.go index 50a5af4ae..97ae1e67b 100644 --- a/meta/example_test.go +++ b/meta/example_test.go @@ -48,6 +48,7 @@ func Example_consumedUnitsCount() { } log.Printf("id=%v, myStr='%s'\n", id, myStr) } + return res.Err() // return finally result error for auto-retry with driver }, table.WithIdempotent(), diff --git a/metrics/driver.go b/metrics/driver.go index 257c2d4e4..5b2f12fbc 100644 --- a/metrics/driver.go +++ b/metrics/driver.go @@ -31,6 +31,7 @@ func driver(config Config) (t trace.Driver) { endpoint = info.Endpoint.Address() nodeID = info.Endpoint.NodeID() ) + return func(info trace.DriverConnInvokeDoneInfo) { if config.Details()&trace.DriverConnEvents != 0 { requests.With(map[string]string{ @@ -46,8 +47,6 @@ func driver(config Config) (t trace.Driver) { } } t.OnConnNewStream = func(info trace.DriverConnNewStreamStartInfo) func( - trace.DriverConnNewStreamRecvInfo, - ) func( trace.DriverConnNewStreamDoneInfo, ) { var ( @@ -55,16 +54,15 @@ func driver(config Config) (t trace.Driver) { endpoint = info.Endpoint.Address() 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() } } } @@ -76,12 +74,14 @@ func driver(config Config) (t trace.Driver) { "cause": errorBrief(info.Cause), }).Add(1) } + return nil } t.OnBalancerClusterDiscoveryAttempt = func(info trace.DriverBalancerClusterDiscoveryAttemptStartInfo) func( trace.DriverBalancerClusterDiscoveryAttemptDoneInfo, ) { eventType := repeater.EventType(*info.Context) + return func(info trace.DriverBalancerClusterDiscoveryAttemptDoneInfo) { balancersDiscoveries.With(map[string]string{ "status": errorBrief(info.Error), @@ -91,6 +91,7 @@ func driver(config Config) (t trace.Driver) { } t.OnBalancerUpdate = func(info trace.DriverBalancerUpdateStartInfo) func(trace.DriverBalancerUpdateDoneInfo) { eventType := repeater.EventType(*info.Context) + return func(info trace.DriverBalancerUpdateDoneInfo) { if config.Details()&trace.DriverBalancerEvents != 0 { balancerUpdates.With(map[string]string{ @@ -126,6 +127,7 @@ func driver(config Config) (t trace.Driver) { t.OnConnDial = func(info trace.DriverConnDialStartInfo) func(trace.DriverConnDialDoneInfo) { endpoint := info.Endpoint.Address() nodeID := info.Endpoint.NodeID() + return func(info trace.DriverConnDialDoneInfo) { if config.Details()&trace.DriverConnEvents != 0 { if info.Error == nil { @@ -144,7 +146,9 @@ func driver(config Config) (t trace.Driver) { "node_id": idToString(info.Endpoint.NodeID()), }).Add(-1) } + return nil } + return t } diff --git a/metrics/error_brief.go b/metrics/error_brief.go index d7fd0e8f2..241687da5 100644 --- a/metrics/error_brief.go +++ b/metrics/error_brief.go @@ -34,6 +34,7 @@ func errorBrief(err error) string { buffer.WriteString(errorBrief(netErr.Err)) buffer.WriteByte(')') } + return buffer.String() } if xerrors.Is(err, context.DeadlineExceeded) { @@ -54,5 +55,6 @@ func errorBrief(err error) string { if ydbErr := xerrors.Error(nil); xerrors.As(err, &ydbErr) { return ydbErr.Name() } + return "unknown" } 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 0670a5d6e..994fd493e 100644 --- a/metrics/retry.go +++ b/metrics/retry.go @@ -11,36 +11,29 @@ 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)) } } } + return t } diff --git a/metrics/sql.go b/metrics/sql.go index a0ff32c69..66b1303a2 100644 --- a/metrics/sql.go +++ b/metrics/sql.go @@ -37,6 +37,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { } } } + return nil } t.OnConnClose = func(info trace.DatabaseSQLConnCloseStartInfo) func(trace.DatabaseSQLConnCloseDoneInfo) { @@ -45,6 +46,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { conns.With(nil).Add(-1) } } + return nil } t.OnConnBegin = func(info trace.DatabaseSQLConnBeginStartInfo) func(trace.DatabaseSQLConnBeginDoneInfo) { @@ -57,10 +59,12 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { txBeginLatency.With(nil).Record(time.Since(start)) } } + return nil } t.OnTxCommit = func(info trace.DatabaseSQLTxCommitStartInfo) func(trace.DatabaseSQLTxCommitDoneInfo) { start := time.Now() + return func(info trace.DatabaseSQLTxCommitDoneInfo) { if config.Details()&trace.DatabaseSQLTxEvents != 0 { txCommit.With(map[string]string{ @@ -72,6 +76,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { } t.OnTxExec = func(info trace.DatabaseSQLTxExecStartInfo) func(trace.DatabaseSQLTxExecDoneInfo) { start := time.Now() + return func(info trace.DatabaseSQLTxExecDoneInfo) { if config.Details()&trace.DatabaseSQLTxEvents != 0 { status := errorBrief(info.Error) @@ -84,6 +89,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { } t.OnTxQuery = func(info trace.DatabaseSQLTxQueryStartInfo) func(trace.DatabaseSQLTxQueryDoneInfo) { start := time.Now() + return func(info trace.DatabaseSQLTxQueryDoneInfo) { if config.Details()&trace.DatabaseSQLTxEvents != 0 { status := errorBrief(info.Error) @@ -96,6 +102,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { } t.OnTxRollback = func(info trace.DatabaseSQLTxRollbackStartInfo) func(trace.DatabaseSQLTxRollbackDoneInfo) { start := time.Now() + return func(info trace.DatabaseSQLTxRollbackDoneInfo) { if config.Details()&trace.DatabaseSQLTxEvents != 0 { txRollback.With(map[string]string{ @@ -113,6 +120,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { mode = info.Mode start = time.Now() ) + return func(info trace.DatabaseSQLConnExecDoneInfo) { if config.Details()&trace.DatabaseSQLEvents != 0 { inflight.With(nil).Add(-1) @@ -137,6 +145,7 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { mode = info.Mode start = time.Now() ) + return func(info trace.DatabaseSQLConnQueryDoneInfo) { if config.Details()&trace.DatabaseSQLEvents != 0 { inflight.With(nil).Add(-1) @@ -153,5 +162,6 @@ func databaseSQL(config Config) (t trace.DatabaseSQL) { } } } + return t } diff --git a/metrics/table.go b/metrics/table.go index 1b04bcc53..80dda3cec 100644 --- a/metrics/table.go +++ b/metrics/table.go @@ -38,6 +38,7 @@ func table(config Config) (t trace.Table) { "node_id": idToString(info.Session.NodeID()), }).Add(-1) } + return nil } t.OnPoolSessionAdd = func(info trace.TablePoolSessionAddInfo) { @@ -54,6 +55,7 @@ func table(config Config) (t trace.Table) { t.OnPoolGet = func(info trace.TablePoolGetStartInfo) func(trace.TablePoolGetDoneInfo) { wait.With(nil).Add(1) start := time.Now() + return func(info trace.TablePoolGetDoneInfo) { wait.With(nil).Add(-1) if info.Error == nil && config.Details()&trace.TablePoolEvents != 0 { @@ -72,7 +74,9 @@ func table(config Config) (t trace.Table) { } inflightLatency.With(nil).Record(time.Since(start.(time.Time))) } + return nil } + return t } diff --git a/metrics/traces.go b/metrics/traces.go index 5fc978606..7744ebbcb 100644 --- a/metrics/traces.go +++ b/metrics/traces.go @@ -9,9 +9,11 @@ func WithTraces(config Config) ydb.Option { return nil } config = config.WithSystem("ydb") + 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 a61ba7178..9734b9b75 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,6 +426,7 @@ 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 } @@ -461,6 +474,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 { 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..9a92449eb --- /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 + // - 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/ratelimiter/example_test.go b/ratelimiter/example_test.go index b64afde4f..12f179bba 100644 --- a/ratelimiter/example_test.go +++ b/ratelimiter/example_test.go @@ -14,6 +14,7 @@ func Example() { 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 @@ -28,6 +29,7 @@ func Example() { }) if err != nil { fmt.Printf("failed to create node: %v", err) + return } defer func() { diff --git a/retry/context.go b/retry/context.go index ed2063070..a3898fc0b 100644 --- a/retry/context.go +++ b/retry/context.go @@ -22,5 +22,6 @@ func WithNonIdempotentOperation(ctx context.Context) context.Context { // Deprecated: context cannot store idempotent value now func IsOperationIdempotent(ctx context.Context) bool { v, ok := ctx.Value(ctxIsOperationIdempotentKey{}).(bool) + return ok && v } diff --git a/retry/errors.go b/retry/errors.go index ed540018f..4c091a720 100644 --- a/retry/errors.go +++ b/retry/errors.go @@ -10,5 +10,6 @@ func unwrapErrBadConn(err error) error { if xerrors.As(err, &e) { return e.Origin() } + return err } diff --git a/retry/errors_data_test.go b/retry/errors_data_test.go index b8dcddcd8..5253b505f 100644 --- a/retry/errors_data_test.go +++ b/retry/errors_data_test.go @@ -19,6 +19,7 @@ func (t idempotency) String() string { if t { return "idempotent" } + return "non-idempotent" } @@ -207,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, @@ -220,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, @@ -408,7 +409,7 @@ var errsToCheck = []struct { backoff: backoff.TypeNoBackoff, deleteSession: true, canRetry: map[idempotency]bool{ - idempotent: false, + idempotent: true, nonIdempotent: false, }, }, 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 50f9c0b99..16b1050bf 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, @@ -256,13 +256,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++ @@ -287,6 +287,7 @@ func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr err } }() } + return op(ctx) }() @@ -328,8 +329,6 @@ func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr err } code = m.StatusCode() - - onIntermediate(err) } } } @@ -337,10 +336,11 @@ func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr err // Check returns retry mode for queryErr. 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 bb9818775..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, ) } @@ -126,6 +126,7 @@ func TestRetryWithCustomErrors(t *testing.T) { if i < limit { return tt.error } + return nil }) if tt.retriable { @@ -147,17 +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() - } - return xerrors.Transport(grpcStatus.Error(code, "")) - }, WithIdempotent(true)) - require.ErrorIs(t, err, context.Canceled) - require.Equal(t, cancelCounterValue, counter) + 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) + }) } } @@ -167,16 +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() - } - return xerrors.Transport(grpcStatus.Error(code, "")) - }, WithIdempotent(true)) - require.ErrorIs(t, err, context.Canceled) - require.Equal(t, cancelCounterValue, counter) + 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) + }) } } diff --git a/retry/retryable_error.go b/retry/retryable_error.go index 441da19ae..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,11 +29,12 @@ 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)) } } + return retryableErrorOptions }()..., ) diff --git a/retry/sql.go b/retry/sql.go index c0525891c..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 @@ -71,6 +71,7 @@ func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Co if err = op(xcontext.MarkRetryCall(ctx), cc); err != nil { return unwrapErrBadConn(xerrors.WithStackTrace(err)) } + return nil }, options.retryOptions...) if err != nil { @@ -78,6 +79,7 @@ func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Co fmt.Errorf("operation failed with %d attempts: %w", attempts, err), ) } + return nil } @@ -127,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, @@ -173,6 +175,7 @@ func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) err if err = tx.Commit(); err != nil { return unwrapErrBadConn(xerrors.WithStackTrace(err)) } + return nil }, options.retryOptions...) if err != nil { @@ -180,5 +183,6 @@ func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) err fmt.Errorf("tx operation failed with %d attempts: %w", attempts, err), ) } + return nil } diff --git a/retry/sql_test.go b/retry/sql_test.go index d4a6c3d5f..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 { @@ -25,12 +24,14 @@ var _ driver.Connector = &mockConnector{} func (m *mockConnector) Open(name string) (driver.Conn, error) { m.t.Log(stack.Record(0)) + return nil, driver.ErrSkip } func (m *mockConnector) Connect(ctx context.Context) (driver.Conn, error) { m.t.Log(stack.Record(0)) m.conns++ + return &mockConn{ t: m.t, queryErr: m.queryErr, @@ -40,6 +41,7 @@ func (m *mockConnector) Connect(ctx context.Context) (driver.Conn, error) { func (m *mockConnector) Driver() driver.Driver { m.t.Log(stack.Record(0)) + return m } @@ -60,6 +62,7 @@ var ( func (m *mockConn) Prepare(query string) (driver.Stmt, error) { m.t.Log(stack.Record(0)) + return nil, driver.ErrSkip } @@ -68,6 +71,7 @@ func (m *mockConn) PrepareContext(ctx context.Context, query string) (driver.Stm if m.closed { return nil, driver.ErrBadConn } + return &mockStmt{ t: m.t, conn: m, @@ -78,11 +82,13 @@ func (m *mockConn) PrepareContext(ctx context.Context, query string) (driver.Stm func (m *mockConn) Close() error { m.t.Log(stack.Record(0)) m.closed = true + return nil } func (m *mockConn) Begin() (driver.Tx, error) { m.t.Log(stack.Record(0)) + return nil, driver.ErrSkip } @@ -91,32 +97,37 @@ func (m *mockConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.T if m.closed { return nil, driver.ErrBadConn } + return m, nil } 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 } + return nil, m.queryErr } 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 } + return nil, m.execErr } func (m *mockConn) Commit() error { m.t.Log(stack.Record(0)) + return nil } func (m *mockConn) Rollback() error { m.t.Log(stack.Record(0)) + return nil } @@ -134,31 +145,37 @@ var ( func (m *mockStmt) Close() error { m.t.Log(stack.Record(0)) + return nil } func (m *mockStmt) NumInput() int { m.t.Log(stack.Record(0)) + return -1 } func (m *mockStmt) Exec(args []driver.Value) (driver.Result, error) { m.t.Log(stack.Record(0)) + return nil, driver.ErrSkip } func (m *mockStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { m.t.Log(stack.Record(0)) + return m.conn.ExecContext(ctx, m.query, args) } func (m *mockStmt) Query(args []driver.Value) (driver.Rows, error) { m.t.Log(stack.Record(0)) + return nil, driver.ErrSkip } func (m *mockStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { m.t.Log(stack.Record(0)) + return m.conn.QueryContext(ctx, m.query, args) } @@ -191,21 +208,12 @@ func TestDoTx(t *testing.T) { defer func() { _ = rows.Close() }() + return rows.Err() }, 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/example_test.go b/scheme/example_test.go index 8f774fd54..3510d7130 100644 --- a/scheme/example_test.go +++ b/scheme/example_test.go @@ -12,6 +12,7 @@ func Example() { 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 diff --git a/scheme/options.go b/scheme/options.go index 1313f84ca..c23cdf900 100644 --- a/scheme/options.go +++ b/scheme/options.go @@ -7,6 +7,7 @@ import ( func permissions(p Permissions) *Ydb_Scheme.Permissions { var y Ydb_Scheme.Permissions p.To(&y) + return &y } diff --git a/scheme/scheme.go b/scheme/scheme.go index 9887a044c..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()), } } @@ -149,13 +149,14 @@ func makePermissions(src []*Ydb_Scheme.Permissions) (dst []Permissions) { for _, p := range src { dst = append(dst, from(p)) } + return dst } func from(y *Ydb_Scheme.Permissions) (p Permissions) { return Permissions{ - Subject: y.Subject, - PermissionNames: y.PermissionNames, + Subject: y.GetSubject(), + PermissionNames: y.GetPermissionNames(), } } @@ -172,5 +173,6 @@ func (p Permissions) To(y *Ydb_Scheme.Permissions) { func InnerConvertEntry(y *Ydb_Scheme.Entry) *Entry { res := &Entry{} res.From(y) + return res } diff --git a/scripting/example_test.go b/scripting/example_test.go index b1b6c76e4..aa96dd85a 100644 --- a/scripting/example_test.go +++ b/scripting/example_test.go @@ -15,6 +15,7 @@ func Example_execute() { 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 @@ -47,6 +48,7 @@ func Example_execute() { if sum != 2 { return fmt.Errorf("unexpected sum: %v", sum) } + return res.Err() }, retry.WithIdempotent(true)); err != nil { fmt.Printf("Execute failed: %v", err) @@ -58,6 +60,7 @@ func Example_streamExecute() { 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 @@ -91,6 +94,7 @@ func Example_streamExecute() { if sum != 2 { return fmt.Errorf("unexpected sum: %v", sum) } + return res.Err() }, retry.WithIdempotent(true)); err != nil { fmt.Printf("StreamExecute failed: %v", err) @@ -102,6 +106,7 @@ func Example_explainPlan() { 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 @@ -112,10 +117,12 @@ func Example_explainPlan() { ) if err != nil { fmt.Printf("Explain failed: %v", err) + return } if res.Plan == "" { fmt.Printf("Unexpected empty plan") + return } fmt.Printf("") @@ -126,6 +133,7 @@ func Example_explainValidate() { 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 @@ -141,6 +149,7 @@ func Example_explainValidate() { if len(res.ParameterTypes) > 0 { return retry.RetryableError(fmt.Errorf("unexpected parameter types")) } + return nil }, retry.WithIdempotent(true)); err != nil { fmt.Printf("Explain failed: %v", err) 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/certificates.go b/sugar/certificates.go index 809a4afd4..5ab386c34 100644 --- a/sugar/certificates.go +++ b/sugar/certificates.go @@ -15,6 +15,7 @@ func LoadCertificatesFromFile(caFile string) ([]*x509.Certificate, error) { if err != nil { return nil, xerrors.WithStackTrace(err) } + return LoadCertificatesFromPem(bytes), nil } @@ -40,5 +41,6 @@ func LoadCertificatesFromPem(bytes []byte) (certs []*x509.Certificate) { } certs = append(certs, cert) } + return } diff --git a/sugar/check_exists.go b/sugar/check_exists.go index 8aeed03c9..59d27e56a 100644 --- a/sugar/check_exists.go +++ b/sugar/check_exists.go @@ -13,6 +13,7 @@ func IsTableExists(ctx context.Context, c scheme.Client, absTablePath string) (e if err != nil { return exists, xerrors.WithStackTrace(err) } + return exists, nil } @@ -21,6 +22,7 @@ func IsColumnTableExists(ctx context.Context, c scheme.Client, absTablePath stri if err != nil { return exists, xerrors.WithStackTrace(err) } + return exists, nil } @@ -31,6 +33,7 @@ func IsEntryExists(ctx context.Context, c scheme.Client, absPath string, entryTy if err != nil { return exists, xerrors.WithStackTrace(err) } + return exists, nil } @@ -39,5 +42,6 @@ func IsDirectoryExists(ctx context.Context, c scheme.Client, absTablePath string if err != nil { return exists, xerrors.WithStackTrace(err) } + return exists, nil } diff --git a/sugar/params.go b/sugar/params.go index e8d6691b1..612268260 100644 --- a/sugar/params.go +++ b/sugar/params.go @@ -3,35 +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)) } @@ -40,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) @@ -48,5 +45,54 @@ func ToYdbParam(param sql.NamedArg) (table.ParameterOption, error) { if len(params) != 1 { return nil, xerrors.WithStackTrace(fmt.Errorf("internal error: wrong parameters count: %v", params)) } + 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/params_test.go b/sugar/params_test.go index b3a6ee10c..8ef2d46bb 100644 --- a/sugar/params_test.go +++ b/sugar/params_test.go @@ -25,6 +25,7 @@ func TestGenerateDeclareSection(t *testing.T) { } } sort.Strings(declares) + return declares } for _, tt := range []struct { @@ -124,6 +125,7 @@ func TestGenerateDeclareSection_ParameterOption(t *testing.T) { } } sort.Strings(declares) + return declares } for _, tt := range []struct { @@ -230,6 +232,7 @@ func TestGenerateDeclareSection_NamedArg(t *testing.T) { } } sort.Strings(declares) + return declares } for _, tt := range []struct { diff --git a/sugar/path.go b/sugar/path.go index 4de49c3fd..cc22e15aa 100644 --- a/sugar/path.go +++ b/sugar/path.go @@ -173,5 +173,6 @@ func RemoveRecursive(ctx context.Context, db dbFoRemoveRecursive, pathToRemove s if !strings.HasPrefix(pathToRemove, db.Name()) { pathToRemove = path.Join(db.Name(), pathToRemove) } + return rmPath(0, pathToRemove) } 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/example_test.go b/table/example_test.go index c02fd1e5f..430c28f84 100644 --- a/table/example_test.go +++ b/table/example_test.go @@ -22,6 +22,7 @@ func Example_select() { 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 @@ -51,6 +52,7 @@ func Example_select() { } fmt.Printf("id=%v, myStr='%s'\n", id, myStr) } + return res.Err() // return finally result error for auto-retry with driver }, table.WithIdempotent(), @@ -65,6 +67,7 @@ func Example_createTable() { 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 @@ -99,6 +102,7 @@ func Example_bulkUpsert() { 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 @@ -135,6 +139,7 @@ func Example_bulkUpsert() { types.StructFieldValue("Message", types.TextValue(msg.Message)), )) } + return s.BulkUpsert(ctx, "/local/bulk_upsert_example", types.ListValue(rows...)) }, table.WithIdempotent(), @@ -149,6 +154,7 @@ func Example_alterTable() { 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 @@ -184,6 +190,7 @@ func Example_lazyTransaction() { db, err := ydb.Open(ctx, "grpc://localhost:2136/local") if err != nil { fmt.Printf("failed connect: %v", err) + return } defer db.Close(ctx) @@ -240,6 +247,7 @@ func Example_lazyTransaction() { if err != nil { return err } + return result.Err() }, table.WithIdempotent(), @@ -254,6 +262,7 @@ func Example_bulkUpsertWithCompression() { 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 @@ -290,6 +299,7 @@ func Example_bulkUpsertWithCompression() { types.StructFieldValue("Message", types.TextValue(msg.Message)), )) } + return s.BulkUpsert(ctx, "/local/bulk_upsert_example", types.ListValue(rows...), options.WithCallOptions(grpc.UseCompressor(gzip.Name)), ) @@ -306,6 +316,7 @@ func Example_dataQueryWithCompression() { 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 @@ -339,6 +350,7 @@ func Example_dataQueryWithCompression() { } fmt.Printf("id=%v, myStr='%s'\n", id, myStr) } + return res.Err() // return finally result error for auto-retry with driver }, table.WithIdempotent(), @@ -353,6 +365,7 @@ func Example_scanQueryWithCompression() { 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 @@ -386,6 +399,7 @@ func Example_scanQueryWithCompression() { } fmt.Printf("id=%v, myStr='%s'\n", id, myStr) } + return res.Err() // return finally result error for auto-retry with driver }, table.WithIdempotent(), @@ -400,6 +414,7 @@ func Example_copyTables() { 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 diff --git a/table/options/models.go b/table/options/models.go index 2f285ad02..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 @@ -117,6 +108,7 @@ func (s StoragePool) toYDB() *Ydb_Table.StoragePool { if s.Media == "" { return nil } + return &Ydb_Table.StoragePool{ Media: s.Media, } @@ -405,8 +397,8 @@ type ( ) type KeyRange struct { - From types.Value - To types.Value + From value.Value + To value.Value } func (kr KeyRange) String() string { @@ -424,6 +416,7 @@ func (kr KeyRange) String() string { buf.WriteString(kr.To.Yql()) } buf.WriteString("]") + return buf.String() } @@ -463,6 +456,7 @@ func NewTTLSettings() TimeToLiveSettings { func (ttl TimeToLiveSettings) ColumnDateType(columnName string) TimeToLiveSettings { ttl.Mode = TimeToLiveModeDateType ttl.ColumnName = columnName + return ttl } @@ -474,6 +468,7 @@ func (ttl TimeToLiveSettings) ColumnSeconds(columnName string) TimeToLiveSetting ttl.Mode = TimeToLiveModeValueSinceUnixEpoch ttl.ColumnName = columnName ttl.ColumnUnit = unitToPointer(TimeToLiveUnitSeconds) + return ttl } @@ -481,6 +476,7 @@ func (ttl TimeToLiveSettings) ColumnMilliseconds(columnName string) TimeToLiveSe ttl.Mode = TimeToLiveModeValueSinceUnixEpoch ttl.ColumnName = columnName ttl.ColumnUnit = unitToPointer(TimeToLiveUnitMilliseconds) + return ttl } @@ -488,6 +484,7 @@ func (ttl TimeToLiveSettings) ColumnMicroseconds(columnName string) TimeToLiveSe ttl.Mode = TimeToLiveModeValueSinceUnixEpoch ttl.ColumnName = columnName ttl.ColumnUnit = unitToPointer(TimeToLiveUnitMicroseconds) + return ttl } @@ -495,11 +492,13 @@ func (ttl TimeToLiveSettings) ColumnNanoseconds(columnName string) TimeToLiveSet ttl.Mode = TimeToLiveModeValueSinceUnixEpoch ttl.ColumnName = columnName ttl.ColumnUnit = unitToPointer(TimeToLiveUnitNanoseconds) + return ttl } func (ttl TimeToLiveSettings) ExpireAfter(expireAfter time.Duration) TimeToLiveSettings { ttl.ExpireAfterSeconds = uint32(expireAfter.Seconds()) + return ttl } diff --git a/table/options/options.go b/table/options/options.go index 62a2a0818..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 { @@ -871,6 +875,7 @@ func WithCallOptions(opts ...grpc.CallOption) withCallOptions { func WithCommit() ExecuteDataQueryOption { return executeDataQueryOptionFunc(func(desc *ExecuteDataQueryDesc, a *allocator.Allocator) []grpc.CallOption { desc.TxControl.CommitTx = true + return nil }) } @@ -879,6 +884,7 @@ func WithCommit() ExecuteDataQueryOption { func WithIgnoreTruncated() ExecuteDataQueryOption { return executeDataQueryOptionFunc(func(desc *ExecuteDataQueryDesc, a *allocator.Allocator) []grpc.CallOption { desc.IgnoreTruncated = true + return nil }) } @@ -915,6 +921,7 @@ func withQueryCachePolicy(opts ...QueryCachePolicyOption) ExecuteDataQueryOption opt((*queryCachePolicy)(d.QueryCachePolicy), a) } } + return nil }) } @@ -934,6 +941,7 @@ func WithCommitCollectStatsModeBasic() CommitTransactionOption { func WithCollectStatsModeNone() ExecuteDataQueryOption { return executeDataQueryOptionFunc(func(d *ExecuteDataQueryDesc, a *allocator.Allocator) []grpc.CallOption { d.CollectStats = Ydb_Table.QueryStatsCollection_STATS_COLLECTION_NONE + return nil }) } @@ -941,6 +949,7 @@ func WithCollectStatsModeNone() ExecuteDataQueryOption { func WithCollectStatsModeBasic() ExecuteDataQueryOption { return executeDataQueryOptionFunc(func(d *ExecuteDataQueryDesc, a *allocator.Allocator) []grpc.CallOption { d.CollectStats = Ydb_Table.QueryStatsCollection_STATS_COLLECTION_BASIC + return nil }) } @@ -969,6 +978,7 @@ var _ ExecuteScanQueryOption = executeScanQueryOptionFunc(nil) func WithExecuteScanQueryMode(m ExecuteScanQueryRequestMode) ExecuteScanQueryOption { return executeScanQueryOptionFunc(func(desc *ExecuteScanQueryDesc) []grpc.CallOption { desc.Mode = m.toYDB() + return nil }) } @@ -999,6 +1009,7 @@ func (stats ExecuteScanQueryStatsType) toYDB() Ydb_Table.QueryStatsCollection_Mo func WithExecuteScanQueryStats(stats ExecuteScanQueryStatsType) ExecuteScanQueryOption { return executeScanQueryOptionFunc(func(desc *ExecuteScanQueryDesc) []grpc.CallOption { desc.CollectStats = stats.toYDB() + return nil }) } @@ -1029,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 ) @@ -1126,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/named/named.go b/table/result/named/named.go index 586a5d077..2d7295a3a 100644 --- a/table/result/named/named.go +++ b/table/result/named/named.go @@ -24,6 +24,7 @@ func Optional(columnName string, destination interface{}) Value { if columnName == "" { panic("columnName must be not empty") } + return Value{ Name: columnName, Value: destination, @@ -38,6 +39,7 @@ func Required(columnName string, destinationValueReference interface{}) Value { if columnName == "" { panic("columnName must be not empty") } + return Value{ Name: columnName, Value: destinationValueReference, @@ -53,6 +55,7 @@ func OptionalWithDefault(columnName string, destinationValueReference interface{ if columnName == "" { panic("columnName must be not empty") } + return Value{ Name: columnName, Value: destinationValueReference, 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 45b81f687..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) @@ -203,6 +200,7 @@ func (t *TransactionSettings) Settings() *Ydb_Table.TransactionSettings { if t == nil { return nil } + return &t.settings } @@ -242,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) } @@ -269,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 @@ -302,6 +300,7 @@ func TxSettings(opts ...TxOption) *TransactionSettings { opt((*txDesc)(&s.settings)) } } + return s } @@ -394,6 +393,7 @@ func (t *TransactionControl) Desc() *Ydb_Table.TransactionControl { if t == nil { return nil } + return &t.desc } @@ -405,6 +405,7 @@ func TxControl(opts ...TxControlOption) *TransactionControl { opt((*txControlDesc)(&c.desc)) } } + return c } @@ -451,107 +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 -} -func (q *QueryParameters) Add(params ...ParameterOption) { - for _, param := range params { - q.m[param.Name()] = param.Value() - } + return &qp } -func ValueParam(name string, v types.Value) ParameterOption { +func ValueParam(name string, v value.Value) ParameterOption { switch len(name) { case 0: panic("empty name") @@ -560,10 +476,8 @@ func ValueParam(name string, v types.Value) ParameterOption { name = "$" + name } } - return ¶meterOption{ - name: name, - value: v, - } + + return params.Named(name, v) } type Options struct { diff --git a/table/types/cast.go b/table/types/cast.go index 574e960fb..71d8476ea 100644 --- a/table/types/cast.go +++ b/table/types/cast.go @@ -15,6 +15,7 @@ func CastTo(v Value, dst interface{}) error { if v == nil { return xerrors.WithStackTrace(errNilValue) } + return value.CastTo(v, dst) } @@ -26,6 +27,7 @@ func IsOptional(t Type) (isOptional bool, innerType Type) { }); isOptional { return isOptional, optionalType.InnerType() } + return false, nil } @@ -38,6 +40,7 @@ func ToDecimal(v Value) (*Decimal, error) { Scale: valuer.Scale(), }, nil } + return nil, xerrors.WithStackTrace(fmt.Errorf("value type '%s' is not decimal type", v.Type().Yql())) } @@ -48,6 +51,7 @@ func ListItems(v Value) ([]Value, error) { }); has { return vv.ListItems(), nil } + return nil, xerrors.WithStackTrace(fmt.Errorf("cannot get list items from '%s'", v.Type().Yql())) } @@ -58,6 +62,7 @@ func TupleItems(v Value) ([]Value, error) { }); has { return vv.TupleItems(), nil } + return nil, xerrors.WithStackTrace(fmt.Errorf("cannot get tuple items from '%s'", v.Type().Yql())) } @@ -68,6 +73,7 @@ func StructFields(v Value) (map[string]Value, error) { }); has { return vv.StructFields(), nil } + return nil, xerrors.WithStackTrace(fmt.Errorf("cannot get struct fields from '%s'", v.Type().Yql())) } @@ -78,8 +84,10 @@ func VariantValue(v Value) (name string, idx uint32, _ Value, _ error) { Value() Value }); has { name, idx := vv.Variant() + return name, idx, vv.Value(), nil } + return "", 0, nil, xerrors.WithStackTrace(fmt.Errorf("cannot get variant value from '%s'", v.Type().Yql())) } @@ -97,5 +105,6 @@ func DictValues(v Value) (map[Value]Value, error) { }); has { return vv.DictValues(), nil } + return nil, xerrors.WithStackTrace(fmt.Errorf("cannot get dict values from '%s'", v.Type().Yql())) } diff --git a/table/types/types.go b/table/types/types.go index a9f587578..cca6b5c09 100644 --- a/table/types/types.go +++ b/table/types/types.go @@ -2,37 +2,36 @@ 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" ) // 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, }) @@ -46,11 +45,12 @@ func Struct(opts ...StructOption) Type { opt(&s) } } - 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 { @@ -60,201 +60,72 @@ func VariantStruct(opts ...StructOption) Type { opt(&s) } } - 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(22, 9) 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 // // Deprecated: use types.Type.Yql() instead -func WriteTypeStringTo(buf *bytes.Buffer, t Type) { +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 f0d730287..9249c4e3c 100644 --- a/table/types/value.go +++ b/table/types/value.go @@ -1,7 +1,6 @@ package types import ( - "fmt" "math/big" "time" @@ -174,20 +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. @@ -230,6 +216,7 @@ func StructValue(opts ...StructValueOption) Value { opt(&p) } } + return value.StructValue(p.fields...) } @@ -252,6 +239,7 @@ func DictValue(opts ...DictValueOption) Value { opt(&p) } } + return value.DictValue(p.fields...) } @@ -264,440 +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_database_sql_bindings_test.go b/tests/integration/basic_example_database_sql_bindings_test.go index 39d955808..b59df9461 100644 --- a/tests/integration/basic_example_database_sql_bindings_test.go +++ b/tests/integration/basic_example_database_sql_bindings_test.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "path" + "sync/atomic" "testing" "time" @@ -17,7 +18,6 @@ import ( "google.golang.org/grpc/metadata" "github.com/ydb-platform/ydb-go-sdk/v3" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" "github.com/ydb-platform/ydb-go-sdk/v3/meta" "github.com/ydb-platform/ydb-go-sdk/v3/retry" @@ -31,7 +31,7 @@ func TestBasicExampleDatabaseSqlBindings(t *testing.T) { ctx, cancel := context.WithTimeout(xtest.Context(t), 42*time.Second) defer cancel() - var totalConsumedUnits xatomic.Uint64 + var totalConsumedUnits atomic.Uint64 defer func() { t.Logf("total consumed units: %d", totalConsumedUnits.Load()) }() diff --git a/tests/integration/basic_example_database_sql_test.go b/tests/integration/basic_example_database_sql_test.go index bf478ecd0..06af67bf9 100644 --- a/tests/integration/basic_example_database_sql_test.go +++ b/tests/integration/basic_example_database_sql_test.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "path" + "sync/atomic" "testing" "time" @@ -17,7 +18,6 @@ import ( "google.golang.org/grpc/metadata" "github.com/ydb-platform/ydb-go-sdk/v3" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" "github.com/ydb-platform/ydb-go-sdk/v3/meta" "github.com/ydb-platform/ydb-go-sdk/v3/retry" @@ -31,7 +31,7 @@ func TestBasicExampleDatabaseSql(t *testing.T) { ctx, cancel := context.WithTimeout(xtest.Context(t), 42*time.Second) defer cancel() - var totalConsumedUnits xatomic.Uint64 + var totalConsumedUnits atomic.Uint64 defer func() { t.Logf("total consumed units: %d", totalConsumedUnits.Load()) }() diff --git a/tests/integration/basic_example_native_test.go b/tests/integration/basic_example_native_test.go index f7d5deb6a..1e4231e5f 100644 --- a/tests/integration/basic_example_native_test.go +++ b/tests/integration/basic_example_native_test.go @@ -13,6 +13,7 @@ import ( "runtime/debug" "strings" "sync" + "sync/atomic" "testing" "time" @@ -24,7 +25,6 @@ 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/internal/xatomic" "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/meta" @@ -160,7 +160,7 @@ func TestBasicExampleNative(t *testing.T) { //nolint:gocyclo ctx, cancel := context.WithTimeout(context.Background(), 42*time.Second) defer cancel() - var totalConsumedUnits xatomic.Uint64 + var totalConsumedUnits atomic.Uint64 defer func() { t.Logf("total consumed units: %d", totalConsumedUnits.Load()) }() @@ -195,7 +195,7 @@ func TestBasicExampleNative(t *testing.T) { //nolint:gocyclo sessionsMtx sync.Mutex sessions = make(map[string]struct{}, limit) - shutdowned xatomic.Bool + shutdowned atomic.Bool shutdownTrace = trace.Table{ OnPoolSessionAdd: func(info trace.TablePoolSessionAddInfo) { @@ -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/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/database_sql_ignore_placeholder_test.go b/tests/integration/database_sql_ignore_placeholder_test.go new file mode 100644 index 000000000..8bc6cac27 --- /dev/null +++ b/tests/integration/database_sql_ignore_placeholder_test.go @@ -0,0 +1,43 @@ +//go:build integration +// +build integration + +package integration + +import ( + "context" + "database/sql" + "testing" + "time" + + "github.com/ydb-platform/ydb-go-sdk/v3" + "github.com/ydb-platform/ydb-go-sdk/v3/retry" +) + +func TestDatabaseSqlDiscardColumn(t *testing.T) { + scope := newScope(t) + db := scope.SQLDriverWithFolder( + ydb.WithTablePathPrefix(scope.Folder()), + ydb.WithAutoDeclare(), + ydb.WithNumericArgs(), + ) + dt := time.Date(2023, 3, 1, 16, 34, 18, 0, time.UTC) + + var row *sql.Row + err := retry.Retry(scope.Ctx, func(ctx context.Context) (err error) { + row = db.QueryRowContext(ctx, ` + SELECT + $1 AS vInt, + $2 AS __discard_column_1, + $3 AS __discard_column_2, + $4 AS __discard_column_3 + `, 1, "2", 3.0, dt, + ) + return row.Err() + }) + scope.Require.NoError(err) + + var resInt int + scope.Require.NoError(row.Scan(&resInt)) + + scope.Require.Equal(1, resInt) +} 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 6a1a6afa4..73363250d 100644 --- a/tests/integration/helpers_test.go +++ b/tests/integration/helpers_test.go @@ -79,7 +79,7 @@ func (scope *scopeT) AuthToken() string { } func (scope *scopeT) Driver(opts ...ydb.Option) *ydb.Driver { - return scope.CacheWithCleanup("", nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[*ydb.Driver], error) { connectionString := scope.ConnectionString() scope.Logf("Connect with connection string: %v", connectionString) @@ -105,8 +105,11 @@ func (scope *scopeT) Driver(opts ...ydb.Option) *ydb.Driver { clean := func() { scope.Require.NoError(driver.Close(scope.Ctx)) } - return driver, clean, err - }).(*ydb.Driver) + + return fixenv.NewGenericResultWithCleanup(driver, clean), err + } + + return fixenv.CacheResult(scope.Env, f) } func (scope *scopeT) SQLDriver(opts ...ydb.ConnectorOption) *sql.DB { @@ -136,7 +139,7 @@ func (scope *scopeT) SQLDriverWithFolder(opts ...ydb.ConnectorOption) *sql.DB { } func (scope *scopeT) Folder() string { - return scope.CacheWithCleanup(nil, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[string], error) { driver := scope.Driver() folderPath := path.Join(driver.Name(), scope.T().Name()) scope.Require.NoError(sugar.RemoveRecursive(scope.Ctx, driver, folderPath)) @@ -148,8 +151,9 @@ func (scope *scopeT) Folder() string { scope.Require.NoError(sugar.RemoveRecursive(scope.Ctx, driver, folderPath)) } } - return folderPath, clean, nil - }).(string) + return fixenv.NewGenericResultWithCleanup(folderPath, clean), nil + } + return fixenv.CacheResult(scope.Env, f) } func (scope *scopeT) Logger() *testLogger { @@ -169,57 +173,62 @@ func (scope *scopeT) TopicConsumerName() string { } func (scope *scopeT) TopicPath() string { - return scope.CacheWithCleanup(nil, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[string], error) { topicName := strings.Replace(scope.T().Name(), "/", "__", -1) topicPath := path.Join(scope.Folder(), topicName) client := scope.Driver().Topic() - cleanup = func() { + cleanup := func() { if !scope.Failed() { _ = client.Drop(scope.Ctx, topicPath) } } cleanup() - err = client.Create(scope.Ctx, topicPath, topicoptions.CreateWithConsumer( + err := client.Create(scope.Ctx, topicPath, topicoptions.CreateWithConsumer( topictypes.Consumer{ Name: scope.TopicConsumerName(), }, )) - return topicPath, cleanup, err - }).(string) + return fixenv.NewGenericResultWithCleanup(topicPath, cleanup), err + } + return fixenv.CacheResult(scope.Env, f) } func (scope *scopeT) TopicReader() *topicreader.Reader { - return scope.CacheWithCleanup(nil, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[*topicreader.Reader], error) { reader, err := scope.Driver().Topic().StartReader( scope.TopicConsumerName(), topicoptions.ReadTopic(scope.TopicPath()), ) - cleanup = func() { + cleanup := func() { if reader != nil { _ = reader.Close(scope.Ctx) } } - return reader, cleanup, err - }).(*topicreader.Reader) + return fixenv.NewGenericResultWithCleanup(reader, cleanup), err + } + + return fixenv.CacheResult(scope.Env, f) } func (scope *scopeT) TopicWriter() *topicwriter.Writer { - return scope.CacheWithCleanup(nil, nil, func() (res interface{}, cleanup fixenv.FixtureCleanupFunc, err error) { + f := func() (*fixenv.GenericResult[*topicwriter.Writer], error) { writer, err := scope.Driver().Topic().StartWriter( scope.TopicPath(), topicoptions.WithWriterProducerID(scope.TopicWriterProducerID()), topicoptions.WithWriterWaitServerAck(true), ) - cleanup = func() { + cleanup := func() { if writer != nil { _ = writer.Close(scope.Ctx) } } - return writer, cleanup, err - }).(*topicwriter.Writer) + return fixenv.NewGenericResultWithCleanup(writer, cleanup), err + } + + return fixenv.CacheResult(scope.Env, f) } func (scope *scopeT) TopicWriterProducerID() string { @@ -266,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/topic_partitions_balanced_test.go b/tests/integration/topic_partitions_balanced_test.go index c0a512a4d..5ad88e15f 100644 --- a/tests/integration/topic_partitions_balanced_test.go +++ b/tests/integration/topic_partitions_balanced_test.go @@ -6,6 +6,7 @@ package integration import ( "context" "sync" + "sync/atomic" "testing" "time" @@ -13,7 +14,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3" "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions" "github.com/ydb-platform/ydb-go-sdk/v3/topic/topictypes" @@ -38,8 +38,8 @@ func TestTopicPartitionsBalanced(t *testing.T) { ) require.NoError(t, err) - var connectedPartitions xatomic.Int64 - var handled xatomic.Int64 + var connectedPartitions atomic.Int64 + var handled atomic.Int64 var sessionsMutex sync.Mutex sessions := map[int64]bool{} diff --git a/tests/integration/topic_read_writer_test.go b/tests/integration/topic_read_writer_test.go index c1e10f9f7..0634c4958 100644 --- a/tests/integration/topic_read_writer_test.go +++ b/tests/integration/topic_read_writer_test.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -24,7 +25,6 @@ import ( "github.com/ydb-platform/ydb-go-sdk/v3/config" "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" "github.com/ydb-platform/ydb-go-sdk/v3/internal/version" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions" "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicsugar" @@ -163,8 +163,6 @@ func TestMessageMetadata(t *testing.T) { } func TestManyConcurentReadersWriters(t *testing.T) { - xtest.AllowByFlag(t, "ISSUE-389") - const partitionCount = 3 const writersCount = 5 const readersCount = 10 @@ -351,8 +349,6 @@ func TestCommitUnexpectedRange(t *testing.T) { } func TestUpdateToken(t *testing.T) { - xtest.AllowByFlag(t, "LOGBROKER-7960") - ctx := context.Background() db := connect(t) dbLogging := connectWithGrpcLogging(t) @@ -376,7 +372,7 @@ func TestUpdateToken(t *testing.T) { var wg sync.WaitGroup wg.Add(1) - stopTopicActivity := xatomic.Bool{} + stopTopicActivity := atomic.Bool{} go func() { defer wg.Done() @@ -391,7 +387,7 @@ func TestUpdateToken(t *testing.T) { } }() - hasMessages := xatomic.Bool{} + hasMessages := atomic.Bool{} wg.Add(1) go func() { diff --git a/tests/integration/topic_regression_test.go b/tests/integration/topic_regression_test.go new file mode 100644 index 000000000..fab6cd075 --- /dev/null +++ b/tests/integration/topic_regression_test.go @@ -0,0 +1,35 @@ +//go:build integration +// +build integration + +package integration + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions" + "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicwriter" +) + +func TestRegressionIssue1011_WriteInitInfoLastSeqNum(t *testing.T) { + scope := newScope(t) + w1 := scope.TopicWriter() + err := w1.Write(scope.Ctx, topicwriter.Message{ + Data: strings.NewReader("123"), + }) + require.NoError(t, err) + require.NoError(t, w1.Close(scope.Ctx)) + + // Check + w2, err := scope.Driver().Topic().StartWriter( + scope.TopicPath(), + topicoptions.WithWriterProducerID(scope.TopicWriterProducerID()), + topicoptions.WithWriterSetAutoSeqNo(false), + ) + require.NoError(t, err) + + info, err := w2.WaitInitInfo(scope.Ctx) + require.Equal(t, int64(1), info.LastSeqNum) +} diff --git a/tests/integration/topic_stress_test.go b/tests/integration/topic_stress_test.go index e55b7a322..f6044e755 100644 --- a/tests/integration/topic_stress_test.go +++ b/tests/integration/topic_stress_test.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -18,7 +19,6 @@ import ( "golang.org/x/sync/errgroup" "github.com/ydb-platform/ydb-go-sdk/v3" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "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/topic/topicoptions" @@ -90,7 +90,7 @@ func stressTestInATopic( writeStatusWriterSeqno := map[string]int64{} readStatusWriterMaxSeqNo := map[string]int64{} - var stopWrite xatomic.Bool + var stopWrite atomic.Bool writeToTopic := func(ctx context.Context, producerID string, wg *sync.WaitGroup) (resErr error) { var writer *topicwriter.Writer 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 0c6bcc606..6468764f0 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 } }, }, @@ -204,6 +200,7 @@ func (s *Storage) createTable(ctx context.Context) error { return retry.Do(ydb.WithTxControl(ctx, writeTx), s.db, func(ctx context.Context, cc *sql.Conn) error { _, err := s.db.ExecContext(ydb.WithQueryMode(ctx, ydb.SchemeQueryMode), s.createQuery) + return err }, retry.WithIdempotent(true), ) @@ -220,6 +217,7 @@ func (s *Storage) dropTable(ctx context.Context) error { return retry.Do(ydb.WithTxControl(ctx, writeTx), s.db, func(ctx context.Context, cc *sql.Conn) error { _, err := s.db.ExecContext(ydb.WithQueryMode(ctx, ydb.SchemeQueryMode), s.dropQuery) + return err }, retry.WithIdempotent(true), ) diff --git a/tests/slo/go.mod b/tests/slo/go.mod index 35a9ce667..e8f010142 100644 --- a/tests/slo/go.mod +++ b/tests/slo/go.mod @@ -1,12 +1,13 @@ module slo -go 1.20 +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 @@ -33,22 +37,24 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/yandex-cloud/go-genproto v0.0.0-20230403093326-123923969dc6 // indirect - github.com/ydb-platform/ydb-go-genproto v0.0.0-20231215113745-46f6d30f974a // indirect + github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf // indirect 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 e12fb847f..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= @@ -737,7 +776,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -780,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= @@ -803,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= @@ -826,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= @@ -916,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= @@ -943,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= @@ -1000,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= @@ -1030,17 +1181,22 @@ 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-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/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= github.com/ydb-platform/ydb-go-sdk-auth-environ v0.2.0/go.mod h1:l6lZ+osdQOjDRBgRA4PQ06BuvmXN2neYjnRw8rCfd7s= github.com/ydb-platform/ydb-go-yc v0.10.2 h1:RAHy6g7ncxk1y0N4oS2MwYXLATqRqKBI6DYXuxpV2wo= @@ -1059,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= @@ -1067,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= @@ -1074,6 +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.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= @@ -1082,22 +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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 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= @@ -1153,15 +1324,21 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +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= @@ -1198,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= @@ -1214,6 +1389,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= @@ -1222,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= @@ -1269,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= @@ -1287,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= @@ -1311,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= @@ -1334,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= @@ -1344,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= @@ -1357,6 +1542,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1364,11 +1550,13 @@ 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= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= @@ -1393,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= @@ -1402,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= @@ -1410,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= @@ -1465,6 +1658,7 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= @@ -1487,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= @@ -1545,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= @@ -1556,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= @@ -1672,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= @@ -1683,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= @@ -1731,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= @@ -1751,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= @@ -1791,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/config/config.go b/tests/slo/internal/config/config.go index 530e412e8..b858d27b8 100644 --- a/tests/slo/internal/config/config.go +++ b/tests/slo/internal/config/config.go @@ -40,6 +40,7 @@ func New() (*Config, error) { if len(os.Args) < 2 { fmt.Print(mainHelp) + return nil, ErrWrongArgs } @@ -49,6 +50,7 @@ func New() (*Config, error) { case "create": if len(os.Args) < 4 { fmt.Print(createHelp) + return nil, ErrWrongArgs } @@ -69,6 +71,7 @@ func New() (*Config, error) { case "cleanup": if len(os.Args) < 4 { fmt.Print(cleanupHelp) + return nil, ErrWrongArgs } @@ -77,6 +80,7 @@ func New() (*Config, error) { case "run": if len(os.Args) < 4 { fmt.Print(runHelp) + return nil, ErrWrongArgs } @@ -98,6 +102,7 @@ func New() (*Config, error) { fs.IntVar(&cfg.ShutdownTime, "shutdown-time", 30, "time to wait before force kill workers") default: fmt.Print(mainHelp) + return nil, ErrWrongArgs } 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/internal/workers/workers.go b/tests/slo/internal/workers/workers.go index 64eb7da79..3aacf2b22 100644 --- a/tests/slo/internal/workers/workers.go +++ b/tests/slo/internal/workers/workers.go @@ -24,6 +24,7 @@ func New(cfg *config.Config, s ReadWriter, label, jobName string) (*Workers, err m, err := metrics.New(cfg.PushGateway, label, jobName) if err != nil { fmt.Printf("create metrics failed: %v\n", err) + return nil, err } diff --git a/tests/slo/internal/workers/write.go b/tests/slo/internal/workers/write.go index daa624174..e9c7b7cae 100644 --- a/tests/slo/internal/workers/write.go +++ b/tests/slo/internal/workers/write.go @@ -28,6 +28,7 @@ func (w *Workers) write(ctx context.Context, gen *generator.Generator) (err erro row, err = gen.Generate() if err != nil { fmt.Printf("generate error: %v\n", err) + return err } 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 4a438bf10..b31f72a0e 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 b6bc813df..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 } }, }, @@ -170,16 +168,15 @@ func (s *Storage) Write(ctx context.Context, row generator.Row) (attempts int, e } _, err = s.x.Context(ctx).SetExpr("hash", fmt.Sprintf("Digest::NumericHash(%d)", row.ID)).Insert(row) + return 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/testutil/compare.go b/testutil/compare.go index 5e56caf94..a021ff909 100644 --- a/testutil/compare.go +++ b/testutil/compare.go @@ -29,34 +29,36 @@ var ErrNotComparable = xerrors.Wrap(fmt.Errorf("not comparable")) func Compare(l, r value.Value) (int, error) { a := allocator.New() defer a.Free() + return compare(unwrapTypedValue(value.ToYDB(l, a)), unwrapTypedValue(value.ToYDB(r, a))) } 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 } } + return &Ydb.TypedValue{Type: typ, Value: val} } 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) @@ -69,6 +71,7 @@ func expandItems(v *Ydb.TypedValue, itemType func(i int) *Ydb.Type) []*Ydb.Typed for i, val := range v.GetValue().GetItems() { values = append(values, unwrapTypedValue(&Ydb.TypedValue{Type: itemType(i), Value: val})) } + return values } @@ -85,12 +88,13 @@ 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 } @@ -99,12 +103,13 @@ 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 } + return -1, nil } if rIsNull { @@ -142,6 +147,7 @@ func compareTuplesOrLists(lhs, rhs []*Ydb.TypedValue) (int, error) { if len(rhs) > len(lhs) { return -1, nil } + return 0, nil } @@ -164,6 +170,7 @@ func compareStructs(lhs, rhs []*Ydb.TypedValue) (int, error) { if len(rhs) > len(lhs) { return -1, nil } + return 0, nil } @@ -271,12 +278,14 @@ func compareDouble(l, r *Ydb.Value) int { func compareText(l, r *Ydb.Value) int { ll := l.GetTextValue() rr := r.GetTextValue() + return strings.Compare(ll, rr) } func compareBytes(l, r *Ydb.Value) int { ll := l.GetBytesValue() rr := r.GetBytesValue() + return bytes.Compare(ll, rr) } @@ -287,11 +296,13 @@ func compareBool(l, r *Ydb.Value) int { if rr { return 0 } + return 1 } if rr { return -1 } + return 0 } @@ -306,6 +317,7 @@ func compareDyNumber(l, r *Ydb.Value) (int, error) { if err != nil { return 0, err } + return lf.Cmp(rf), 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/driver.go b/testutil/driver.go index 6be8849e8..48b82d4ca 100644 --- a/testutil/driver.go +++ b/testutil/driver.go @@ -24,6 +24,7 @@ func (m MethodCode) String() string { if method, ok := codeToString[m]; ok { return method } + return "" } @@ -33,6 +34,7 @@ func (m Method) Code() MethodCode { if code, ok := grpcMethodToCode[m]; ok { return code } + return UnknownMethod } @@ -150,6 +152,7 @@ func (b *balancerStub) Invoke( if b.onInvoke == nil { return fmt.Errorf("database.onInvoke() not defined") } + return b.onInvoke(ctx, method, args, reply, opts...) } @@ -162,6 +165,7 @@ func (b *balancerStub) NewStream( if b.onNewStream == nil { return nil, fmt.Errorf("database.onNewStream() not defined") } + return b.onNewStream(ctx, desc, method, opts...) } @@ -170,6 +174,7 @@ func (b *balancerStub) Get(context.Context) (conn grpc.ClientConnInterface, err onInvoke: b.onInvoke, onNewStream: b.onNewStream, } + return cc, nil } @@ -215,8 +220,10 @@ func WithInvokeHandlers(invokeHandlers InvokeHandlers) balancerOption { Result: anyResult, }, ) + return nil } + return fmt.Errorf("method '%s' not implemented", method) } } @@ -233,6 +240,7 @@ func WithNewStreamHandlers(newStreamHandlers NewStreamHandlers) balancerOption { if handler, ok := newStreamHandlers[Method(method).Code()]; ok { return handler(desc) } + return nil, fmt.Errorf("method '%s' not implemented", method) } } @@ -245,6 +253,7 @@ func NewBalancer(opts ...balancerOption) *balancerStub { opt(c) } } + return c } @@ -272,6 +281,7 @@ func (c *clientConn) Address() string { if c.onAddress != nil { return c.onAddress() } + return "" } @@ -285,6 +295,7 @@ func (c *clientConn) Invoke( if c.onInvoke == nil { return fmt.Errorf("onInvoke not implemented (method: %s, request: %v, response: %v)", method, args, reply) } + return c.onInvoke(ctx, method, args, reply, opts...) } @@ -297,6 +308,7 @@ func (c *clientConn) NewStream( if c.onNewStream == nil { return nil, fmt.Errorf("onNewStream not implemented (method: %s, desc: %v)", method, desc) } + return c.onNewStream(ctx, desc, method, opts...) } @@ -313,6 +325,7 @@ func (s *ClientStream) Header() (metadata.MD, error) { if s.OnHeader == nil { return nil, xerrors.WithStackTrace(ErrNotImplemented) } + return s.OnHeader() } @@ -320,6 +333,7 @@ func (s *ClientStream) Trailer() metadata.MD { if s.OnTrailer == nil { return nil } + return s.OnTrailer() } @@ -327,6 +341,7 @@ func (s *ClientStream) CloseSend() error { if s.OnCloseSend == nil { return xerrors.WithStackTrace(ErrNotImplemented) } + return s.OnCloseSend() } @@ -334,6 +349,7 @@ func (s *ClientStream) Context() context.Context { if s.OnContext == nil { return nil } + return s.OnContext() } @@ -341,6 +357,7 @@ func (s *ClientStream) SendMsg(m interface{}) error { if s.OnSendMsg == nil { return xerrors.WithStackTrace(ErrNotImplemented) } + return s.OnSendMsg(m) } @@ -348,10 +365,12 @@ func (s *ClientStream) RecvMsg(m interface{}) error { if s.OnRecvMsg == nil { return xerrors.WithStackTrace(ErrNotImplemented) } + return s.OnRecvMsg(m) } func lastSegment(m string) string { s := strings.Split(m, "/") + return s[len(s)-1] } diff --git a/testutil/file_line.go b/testutil/file_line.go index 37be268b5..5bc37c4f5 100644 --- a/testutil/file_line.go +++ b/testutil/file_line.go @@ -8,5 +8,6 @@ import ( func FileLine(skip int) string { _, file, line, _ := runtime.Caller(skip) + return filepath.Base(file) + ":" + strconv.Itoa(line) } diff --git a/testutil/session.go b/testutil/session.go index cdc1c5338..d5ca81899 100644 --- a/testutil/session.go +++ b/testutil/session.go @@ -35,10 +35,11 @@ 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) } } + return fmt.Sprintf("ydb://session/%d?node_id=%d&id=%s==", h.serviceID, h.nodeID, h.hash) } diff --git a/topic/example_test.go b/topic/example_test.go index bba68c4be..9df1fab42 100644 --- a/topic/example_test.go +++ b/topic/example_test.go @@ -21,6 +21,7 @@ func Example_createTopic() { db, err := ydb.Open(ctx, connectionString) if err != nil { log.Printf("failed connect: %v", err) + return } defer db.Close(ctx) // cleanup resources @@ -35,6 +36,7 @@ func Example_createTopic() { ) if err != nil { log.Printf("failed create topic: %v", err) + return } } @@ -48,6 +50,7 @@ func Example_alterTopic() { db, err := ydb.Open(ctx, connectionString) if err != nil { log.Printf("failed connect: %v", err) + return } defer db.Close(ctx) // cleanup resources @@ -60,6 +63,7 @@ func Example_alterTopic() { ) if err != nil { log.Printf("failed alter topic: %v", err) + return } } @@ -73,6 +77,7 @@ func Example_describeTopic() { db, err := ydb.Open(ctx, connectionString) if err != nil { log.Printf("failed connect: %v", err) + return } defer db.Close(ctx) // cleanup resources @@ -80,6 +85,7 @@ func Example_describeTopic() { descResult, err := db.Topic().Describe(ctx, "topic-path") if err != nil { log.Printf("failed drop topic: %v", err) + return } fmt.Printf("describe: %#v\n", descResult) @@ -94,6 +100,7 @@ func Example_dropTopic() { db, err := ydb.Open(ctx, connectionString) if err != nil { log.Printf("failed connect: %v", err) + return } defer db.Close(ctx) // cleanup resources @@ -101,6 +108,7 @@ func Example_dropTopic() { err = db.Topic().Drop(ctx, "topic-path") if err != nil { log.Printf("failed drop topic: %v", err) + return } } @@ -114,6 +122,7 @@ func Example_readMessage() { db, err := ydb.Open(ctx, connectionString) if err != nil { log.Printf("failed connect: %v", err) + return } defer db.Close(ctx) // cleanup resources @@ -121,6 +130,7 @@ func Example_readMessage() { reader, err := db.Topic().StartReader("consumer", topicoptions.ReadTopic("/topic/path")) if err != nil { fmt.Printf("failed start reader: %v", err) + return } @@ -128,12 +138,14 @@ func Example_readMessage() { mess, err := reader.ReadMessage(ctx) if err != nil { fmt.Printf("failed start reader: %v", err) + return } content, err := io.ReadAll(mess) if err != nil { fmt.Printf("failed start reader: %v", err) + return } fmt.Println(string(content)) diff --git a/topic/topicoptions/topicoptions_alter.go b/topic/topicoptions/topicoptions_alter.go index 3667e0efa..52700a7f4 100644 --- a/topic/topicoptions/topicoptions_alter.go +++ b/topic/topicoptions/topicoptions_alter.go @@ -43,6 +43,7 @@ func AlterWithSupportedCodecs(codecs ...topictypes.Codec) AlterOption { sort.Slice(codecs, func(i, j int) bool { return codecs[i] < codecs[j] }) + return withSupportedCodecs(codecs) } @@ -66,12 +67,14 @@ func AlterWithAddConsumers(consumers ...topictypes.Consumer) AlterOption { sort.Slice(consumers, func(i, j int) bool { return consumers[i].Name < consumers[j].Name }) + return withAddConsumers(consumers) } // AlterWithDropConsumers drop consumer from the topic func AlterWithDropConsumers(consumersName ...string) AlterOption { sort.Strings(consumersName) + return withDropConsumers(consumersName) } @@ -96,6 +99,7 @@ func AlterConsumerWithSupportedCodecs(name string, codecs []topictypes.Codec) Al sort.Slice(codecs, func(i, j int) bool { return codecs[i] < codecs[j] }) + return withConsumerWithSupportedCodecs{ name: name, codecs: codecs, @@ -123,5 +127,6 @@ func ensureAlterConsumer( } } consumers = append(consumers, rawtopic.AlterConsumer{Name: name}) + return consumers, len(consumers) - 1 } diff --git a/topic/topicoptions/topicoptions_create.go b/topic/topicoptions/topicoptions_create.go index cf111234f..6fa1cb4dc 100644 --- a/topic/topicoptions/topicoptions_create.go +++ b/topic/topicoptions/topicoptions_create.go @@ -43,6 +43,7 @@ func CreateWithSupportedCodecs(codecs ...topictypes.Codec) CreateOption { sort.Slice(codecs, func(i, j int) bool { return codecs[i] < codecs[j] }) + return withSupportedCodecs(codecs) } @@ -66,5 +67,6 @@ func CreateWithConsumer(consumers ...topictypes.Consumer) CreateOption { sort.Slice(consumers, func(i, j int) bool { return consumers[i].Name < consumers[j].Name }) + return withAddConsumers(consumers) } diff --git a/topic/topicreader/batch_options.go b/topic/topicreader/batch_options.go index b4c670c31..166432535 100644 --- a/topic/topicreader/batch_options.go +++ b/topic/topicreader/batch_options.go @@ -12,6 +12,7 @@ func (count WithBatchMaxCount) Apply( options topicreaderinternal.ReadMessageBatchOptions, ) topicreaderinternal.ReadMessageBatchOptions { options.MaxCount = int(count) + return options } @@ -33,5 +34,6 @@ func (count WithBatchPreferMinCount) Apply( panic("ydb: min batch size must be 1 or greater") } options.MinCount = int(count) + return options } 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/topic/topicreader/reader.go b/topic/topicreader/reader.go index cd6831f7e..9c50d4a51 100644 --- a/topic/topicreader/reader.go +++ b/topic/topicreader/reader.go @@ -2,9 +2,9 @@ package topicreader import ( "context" + "sync/atomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/topic/topicreaderinternal" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" ) @@ -21,8 +21,8 @@ import ( // | Close | - | - | - | - | type Reader struct { reader topicreaderinternal.Reader - readInFlyght xatomic.Bool - commitInFlyght xatomic.Bool + readInFlyght atomic.Bool + commitInFlyght atomic.Bool } // NewReader @@ -124,7 +124,7 @@ func (r *Reader) Close(ctx context.Context) error { return r.reader.Close(ctx) } -func (r *Reader) inCall(inFlight *xatomic.Bool) error { +func (r *Reader) inCall(inFlight *atomic.Bool) error { if inFlight.CompareAndSwap(false, true) { return nil } @@ -132,7 +132,7 @@ func (r *Reader) inCall(inFlight *xatomic.Bool) error { return xerrors.WithStackTrace(ErrConcurrencyCall) } -func (r *Reader) outCall(inFlight *xatomic.Bool) { +func (r *Reader) outCall(inFlight *atomic.Bool) { if inFlight.CompareAndSwap(true, false) { return } diff --git a/topic/topicwriter/topicwriter.go b/topic/topicwriter/topicwriter.go index 341e16da4..a9b5971f3 100644 --- a/topic/topicwriter/topicwriter.go +++ b/topic/topicwriter/topicwriter.go @@ -50,6 +50,7 @@ func (w *Writer) WaitInit(ctx context.Context) (err error) { if err != nil { return err } + return nil } @@ -61,6 +62,7 @@ func (w *Writer) WaitInitInfo(ctx context.Context) (info PublicInitialInfo, err return PublicInitialInfo{}, err } publicInfo := PublicInitialInfo{LastSeqNum: privateInfo.LastSeqNum} + return publicInfo, nil } diff --git a/trace/details.go b/trace/details.go index c1617aa2d..124885135 100644 --- a/trace/details.go +++ b/trace/details.go @@ -24,6 +24,7 @@ func (d Details) String() string { } } sort.Strings(ss) + return strings.Join(ss, "|") } @@ -43,6 +44,11 @@ const ( TablePoolSessionLifeCycleEvents TablePoolAPIEvents + QuerySessionEvents + QueryResultEvents + QueryTransactionEvents + QueryPoolEvents + TopicControlPlaneEvents TopicReaderCustomerEvents @@ -72,9 +78,6 @@ const ( CoordinationEvents - // Deprecated: has no effect now - DriverClusterEvents - DriverEvents = DriverConnEvents | DriverBalancerEvents | DriverResolverEvents | @@ -89,6 +92,11 @@ const ( TablePoolSessionLifeCycleEvents | TablePoolAPIEvents + QueryEvents = QuerySessionEvents | + QueryPoolEvents | + QueryResultEvents | + QueryTransactionEvents + TablePoolEvents = TablePoolLifeCycleEvents | TablePoolSessionLifeCycleEvents | TablePoolAPIEvents @@ -143,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", @@ -190,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 { @@ -211,5 +225,6 @@ func MatchDetails(pattern string, opts ...matchDetailsOption) (d Details) { if d == 0 { return h.defaultDetails } + return d } 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 5c31cfbd5..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, @@ -90,12 +70,14 @@ type Method string // Name returns the rpc method name. func (m Method) Name() (s string) { _, s = m.Split() + return } // Service returns the rpc service name. func (m Method) Service() (s string) { s, _ = m.Split() + return } @@ -112,6 +94,7 @@ func (m Method) Split() (service, method string) { if i == -1 { return string(m), string(m) } + return strings.TrimPrefix(string(m[:i]), "/"), string(m[i+1:]) } @@ -169,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. @@ -323,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/trace/trace_test.go b/trace/trace_test.go index 6cb8a5b1b..fbdf19668 100644 --- a/trace/trace_test.go +++ b/trace/trace_test.go @@ -69,6 +69,7 @@ func stubEachFunc(x reflect.Value) map[string]bool { fs[name] = true }, }).Stub(x) + return fs } diff --git a/trace/traceutil.go b/trace/traceutil.go index 613e0b237..8ad52bfb6 100644 --- a/trace/traceutil.go +++ b/trace/traceutil.go @@ -23,6 +23,7 @@ func ClearContext(x interface{}) interface{} { c.Set(reflect.Zero(c.Type())) p.Set(x) } + return p.Interface() } @@ -69,6 +70,7 @@ func (f FieldStubber) Stub(x reflect.Value) { params[i] = arg.Interface() } f.OnCall(name, params...) + return out }) fx.Set(fn) diff --git a/with.go b/with.go index f8a39d594..c226db991 100644 --- a/with.go +++ b/with.go @@ -2,14 +2,14 @@ package ydb import ( "context" + "sync/atomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" - "github.com/ydb-platform/ydb-go-sdk/v3/internal/xatomic" "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" "github.com/ydb-platform/ydb-go-sdk/v3/trace" ) -var nextID xatomic.Uint64 //nolint:gochecknoglobals +var nextID atomic.Uint64 //nolint:gochecknoglobals func (d *Driver) with(ctx context.Context, opts ...Option) (*Driver, uint64, error) { id := nextID.Add(1) @@ -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() {