diff --git a/.github/workflows/spanner-lib-tests.yml b/.github/workflows/spanner-lib-tests.yml new file mode 100644 index 00000000..8a51445d --- /dev/null +++ b/.github/workflows/spanner-lib-tests.yml @@ -0,0 +1,89 @@ +on: + push: + branches: [ main ] + pull_request: +permissions: + contents: read + pull-requests: write +name: Spanner Lib Tests +jobs: + test: + strategy: + matrix: + go-version: [1.24.x] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v4 + - name: Run unit tests + working-directory: spannerlib/lib + run: go test -race -short + + build-lib: + strategy: + matrix: + go-version: [1.24.x] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v4 + - name: Build shared lib + working-directory: spannerlib/shared + run: go build -o spannerlib.so -buildmode=c-shared shared_lib.go + + test-dotnet-ubuntu: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + - name: Checkout code + uses: actions/checkout@v4 + - name: Build shared lib + working-directory: spannerlib/shared + run: go build -o spannerlib.so -buildmode=c-shared shared_lib.go + - name: Copy lib to dotnet folder + working-directory: spannerlib + run: | + mkdir -p dotnet-spannerlib/Google.Cloud.SpannerLib.Native/libraries/any + cp shared/spannerlib.so dotnet-spannerlib/Google.Cloud.SpannerLib.Native/libraries/any/spannerlib.so + - name: Build gRPC server + working-directory: spannerlib/grpc-server + run: go build grpc_server.go + - name: Copy gRPC server to dotnet folder + working-directory: spannerlib + run: | + mkdir -p dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/binaries/any + cp grpc-server/grpc_server dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/binaries/any/grpc_server + - name: Install dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + - name: dotnet version + run: dotnet --version + - name: Build native library package + run: dotnet pack + working-directory: spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native + - name: Add package source + run: dotnet nuget add source "$PWD"/bin/Release --name local + working-directory: spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native + - name: Restore dependencies + run: dotnet restore + working-directory: spannerlib/dotnet-spannerlib + - name: Build + run: dotnet build --no-restore + working-directory: spannerlib/dotnet-spannerlib + - name: Unit Tests + working-directory: spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests + run: dotnet test --no-build --verbosity normal diff --git a/checksum_row_iterator.go b/checksum_row_iterator.go index 623832a1..c6980a27 100644 --- a/checksum_row_iterator.go +++ b/checksum_row_iterator.go @@ -23,6 +23,7 @@ import ( "cloud.google.com/go/spanner" sppb "cloud.google.com/go/spanner/apiv1/spannerpb" + "github.com/googleapis/go-sql-spanner/parser" "google.golang.org/api/iterator" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -51,10 +52,11 @@ type checksumRowIterator struct { *spanner.RowIterator metadata *sppb.ResultSetMetadata - ctx context.Context - tx *readWriteTransaction - stmt spanner.Statement - options spanner.QueryOptions + ctx context.Context + tx *readWriteTransaction + stmt spanner.Statement + stmtType parser.StatementType + options spanner.QueryOptions // nc (nextCount) indicates the number of times that next has been called // on the iterator. Next() will be called the same number of times during // a retry. @@ -253,10 +255,5 @@ func (it *checksumRowIterator) Metadata() (*sppb.ResultSetMetadata, error) { } func (it *checksumRowIterator) ResultSetStats() *sppb.ResultSetStats { - // TODO: The Spanner client library should offer an option to get the full - // ResultSetStats, instead of only the RowCount and QueryPlan. - return &sppb.ResultSetStats{ - RowCount: &sppb.ResultSetStats_RowCountExact{RowCountExact: it.RowIterator.RowCount}, - QueryPlan: it.RowIterator.QueryPlan, - } + return createResultSetStats(it.RowIterator, it.stmtType) } diff --git a/conn.go b/conn.go index 7f9ece77..36adb89d 100644 --- a/conn.go +++ b/conn.go @@ -222,6 +222,9 @@ type SpannerConn interface { // return the same Spanner client. UnderlyingClient() (client *spanner.Client, err error) + // DetectStatementType returns the type of SQL statement. + DetectStatementType(query string) parser.StatementType + // resetTransactionForRetry resets the current transaction after it has // been aborted by Spanner. Calling this function on a transaction that // has not been aborted is not supported and will cause an error to be @@ -280,12 +283,18 @@ type conn struct { // tempBatchReadOnlyTransactionOptions are temporarily set right before a // batch read-only transaction is started on a Spanner connection. tempBatchReadOnlyTransactionOptions *BatchReadOnlyTransactionOptions + tempProtoTransactionOptions *spannerpb.TransactionOptions } func (c *conn) UnderlyingClient() (*spanner.Client, error) { return c.client, nil } +func (c *conn) DetectStatementType(query string) parser.StatementType { + info := c.parser.DetectStatementType(query) + return info.StatementType +} + func (c *conn) CommitTimestamp() (time.Time, error) { ts := propertyCommitTimestamp.GetValueOrDefault(c.state) if ts == nil { @@ -389,6 +398,16 @@ func (c *conn) ReadOnlyStaleness() spanner.TimestampBound { return propertyReadOnlyStaleness.GetValueOrDefault(c.state) } +func (c *conn) readOnlyStalenessPointer() *spanner.TimestampBound { + val := propertyReadOnlyStaleness.GetConnectionPropertyValue(c.state) + if val == nil || !val.HasValue() { + return nil + } + staleness, _ := val.GetValue() + timestampBound := staleness.(spanner.TimestampBound) + return ×tampBound +} + func (c *conn) SetReadOnlyStaleness(staleness spanner.TimestampBound) error { _, err := c.setReadOnlyStaleness(staleness) return err @@ -675,6 +694,17 @@ func sum(affected []int64) int64 { return sum } +func (c *conn) WriteMutations(ctx context.Context, ms []*spanner.Mutation) (*spanner.CommitResponse, error) { + if c.inTransaction() { + return nil, c.BufferWrite(ms) + } + ts, err := c.Apply(ctx, ms) + if err != nil { + return nil, err + } + return &spanner.CommitResponse{CommitTs: ts}, nil +} + func (c *conn) Apply(ctx context.Context, ms []*spanner.Mutation, opts ...spanner.ApplyOption) (commitTimestamp time.Time, err error) { if c.inTransaction() { return time.Time{}, spanner.ToSpannerError( @@ -858,13 +888,13 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions *Exec // The statement was either detected as being a query, or potentially not recognized at all. // In that case, just default to using a single-use read-only transaction and let Spanner // return an error if the statement is not suited for that type of transaction. - iter = &readOnlyRowIterator{c.execSingleQuery(ctx, c.client, stmt, c.ReadOnlyStaleness(), execOptions)} + iter = &readOnlyRowIterator{c.execSingleQuery(ctx, c.client, stmt, c.ReadOnlyStaleness(), execOptions), statementType.StatementType} } } else { if execOptions.PartitionedQueryOptions.PartitionQuery { return c.tx.partitionQuery(ctx, stmt, execOptions) } - iter, err = c.tx.Query(ctx, stmt, execOptions) + iter, err = c.tx.Query(ctx, stmt, statementType.StatementType, execOptions) if err != nil { return nil, err } @@ -987,6 +1017,7 @@ func (c *conn) options(reset bool) *ExecOptions { }, }, PartitionedQueryOptions: PartitionedQueryOptions{}, + TimestampBound: c.readOnlyStalenessPointer(), } if c.tempExecOptions != nil { effectiveOptions.merge(c.tempExecOptions) @@ -1071,6 +1102,26 @@ func (c *conn) getBatchReadOnlyTransactionOptions() BatchReadOnlyTransactionOpti return BatchReadOnlyTransactionOptions{TimestampBound: c.ReadOnlyStaleness()} } +func (c *conn) BeginReadOnlyTransaction(ctx context.Context, options *ReadOnlyTransactionOptions) (driver.Tx, error) { + c.withTempReadOnlyTransactionOptions(options) + tx, err := c.BeginTx(ctx, driver.TxOptions{ReadOnly: true}) + if err != nil { + c.withTempReadOnlyTransactionOptions(nil) + return nil, err + } + return tx, nil +} + +func (c *conn) BeginReadWriteTransaction(ctx context.Context, options *ReadWriteTransactionOptions) (driver.Tx, error) { + c.withTempTransactionOptions(options) + tx, err := c.BeginTx(ctx, driver.TxOptions{}) + if err != nil { + c.withTempTransactionOptions(nil) + return nil, err + } + return tx, nil +} + func (c *conn) Begin() (driver.Tx, error) { return c.BeginTx(context.Background(), driver.TxOptions{}) } @@ -1254,7 +1305,7 @@ func (c *conn) inReadWriteTransaction() bool { return false } -func (c *conn) commit(ctx context.Context) (*spanner.CommitResponse, error) { +func (c *conn) Commit(ctx context.Context) (*spanner.CommitResponse, error) { if !c.inTransaction() { return nil, status.Errorf(codes.FailedPrecondition, "this connection does not have a transaction") } @@ -1262,10 +1313,13 @@ func (c *conn) commit(ctx context.Context) (*spanner.CommitResponse, error) { if err := c.tx.Commit(); err != nil { return nil, err } - return c.CommitResponse() + + // This will return either the commit response or nil, depending on whether the transaction was a + // read/write transaction or a read-only transaction. + return propertyCommitResponse.GetValueOrDefault(c.state), nil } -func (c *conn) rollback(ctx context.Context) error { +func (c *conn) Rollback(ctx context.Context) error { if !c.inTransaction() { return status.Errorf(codes.FailedPrecondition, "this connection does not have a transaction") } @@ -1274,6 +1328,9 @@ func (c *conn) rollback(ctx context.Context) error { } func queryInSingleUse(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator { + if options.TimestampBound != nil { + tb = *options.TimestampBound + } return c.Single().WithTimestampBound(tb).QueryWithOptions(ctx, statement, options.QueryOptions) } diff --git a/driver.go b/driver.go index 15cec71a..50cfc7dc 100644 --- a/driver.go +++ b/driver.go @@ -173,6 +173,10 @@ type ExecOptions struct { TransactionOptions spanner.TransactionOptions // QueryOptions are the query options that will be used for the statement. QueryOptions spanner.QueryOptions + // TimestampBound is the timestamp bound that will be used for the statement + // if it is a query outside a transaction. Setting this option will override + // the default TimestampBound that is set on the connection. + TimestampBound *spanner.TimestampBound // PartitionedQueryOptions are used for partitioned queries, and ignored // for all other statements. @@ -234,6 +238,9 @@ func (dest *ExecOptions) merge(src *ExecOptions) { if src.AutocommitDMLMode != Unspecified { dest.AutocommitDMLMode = src.AutocommitDMLMode } + if src.TimestampBound != nil { + dest.TimestampBound = src.TimestampBound + } (&dest.PartitionedQueryOptions).merge(&src.PartitionedQueryOptions) mergeQueryOptions(&dest.QueryOptions, &src.QueryOptions) mergeTransactionOptions(&dest.TransactionOptions, &src.TransactionOptions) @@ -1143,6 +1150,10 @@ func BeginReadWriteTransaction(ctx context.Context, db *sql.DB, options ReadWrit // be active when we hit this point. go conn.Close() } + return BeginReadWriteTransactionOnConn(ctx, conn, options) +} + +func BeginReadWriteTransactionOnConn(ctx context.Context, conn *sql.Conn, options ReadWriteTransactionOptions) (*sql.Tx, error) { if err := withTempReadWriteTransactionOptions(conn, &options); err != nil { return nil, err } @@ -1195,6 +1206,10 @@ func BeginReadOnlyTransaction(ctx context.Context, db *sql.DB, options ReadOnlyT // be active when we hit this point. go conn.Close() } + return BeginReadOnlyTransactionOnConn(ctx, conn, options) +} + +func BeginReadOnlyTransactionOnConn(ctx context.Context, conn *sql.Conn, options ReadOnlyTransactionOptions) (*sql.Tx, error) { if err := withTempReadOnlyTransactionOptions(conn, &options); err != nil { return nil, err } diff --git a/driver_test.go b/driver_test.go index 8e62281a..f095f0e8 100644 --- a/driver_test.go +++ b/driver_test.go @@ -631,7 +631,9 @@ func TestConn_NonDdlStatementsInDdlBatch(t *testing.T) { state: createInitialConnectionState(connectionstate.TypeNonTransactional, map[string]connectionstate.ConnectionPropertyValue{}), batch: &batch{tp: parser.BatchTypeDdl}, execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator { - return &spanner.RowIterator{} + return &spanner.RowIterator{ + Metadata: &spannerpb.ResultSetMetadata{}, + } }, execSingleDMLTransactional: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (*result, *spanner.CommitResponse, error) { return &result{}, &spanner.CommitResponse{}, nil diff --git a/driver_with_mockserver_test.go b/driver_with_mockserver_test.go index a8787d3b..8ebe91dc 100644 --- a/driver_with_mockserver_test.go +++ b/driver_with_mockserver_test.go @@ -5531,9 +5531,8 @@ func setupTestDBConnectionWithParams(t *testing.T, params string) (db *sql.DB, s func setupTestDBConnectionWithParamsAndDialect(t *testing.T, params string, dialect databasepb.DatabaseDialect) (db *sql.DB, server *testutil.MockedSpannerInMemTestServer, teardown func()) { server, _, serverTeardown := setupMockedTestServerWithDialect(t, dialect) - db, err := sql.Open( - "spanner", - fmt.Sprintf("%s/projects/p/instances/i/databases/d?useplaintext=true;%s", server.Address, params)) + dsn := fmt.Sprintf("%s/projects/p/instances/i/databases/d?useplaintext=true;%s", server.Address, params) + db, err := sql.Open("spanner", dsn) if err != nil { serverTeardown() t.Fatal(err) diff --git a/partitioned_query.go b/partitioned_query.go index 77fe8240..018f7b7d 100644 --- a/partitioned_query.go +++ b/partitioned_query.go @@ -22,6 +22,7 @@ import ( "io" "cloud.google.com/go/spanner" + "github.com/googleapis/go-sql-spanner/parser" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -231,7 +232,7 @@ func (pq *PartitionedQuery) execute(ctx context.Context, index int) (*rows, erro return nil, spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "invalid partition index: %d", index)) } spannerIter := pq.tx.Execute(ctx, pq.Partitions[index]) - iter := &readOnlyRowIterator{spannerIter} + iter := &readOnlyRowIterator{spannerIter, parser.StatementTypeQuery} return &rows{it: iter, decodeOption: pq.execOptions.DecodeOption}, nil } diff --git a/spannerlib/.gitignore b/spannerlib/.gitignore new file mode 100644 index 00000000..f6dbcd3d --- /dev/null +++ b/spannerlib/.gitignore @@ -0,0 +1,3 @@ +spannerlib.h +spannerlib.so +grpc_server diff --git a/spannerlib/README.md b/spannerlib/README.md new file mode 100644 index 00000000..e84043c7 --- /dev/null +++ b/spannerlib/README.md @@ -0,0 +1,6 @@ +# Shared Library (Internal) + +__This module can receive breaking changes without prior notice.__ + +This is an internal module that is used to expose the features in the database/sql driver +to drivers in other programming languages. diff --git a/spannerlib/api/README.md b/spannerlib/api/README.md new file mode 100644 index 00000000..9642ee56 --- /dev/null +++ b/spannerlib/api/README.md @@ -0,0 +1,6 @@ +# Shared Library (Internal) + +__This module can receive breaking changes without prior notice.__ + +This is the common internal API for the various external APIs for exposing the +features in the database/sql driver to drivers in other programming languages. diff --git a/spannerlib/api/connection.go b/spannerlib/api/connection.go new file mode 100644 index 00000000..1c8d5093 --- /dev/null +++ b/spannerlib/api/connection.go @@ -0,0 +1,407 @@ +package api + +import ( + "context" + "database/sql" + "database/sql/driver" + "fmt" + "strings" + "sync" + "sync/atomic" + + "cloud.google.com/go/spanner" + "cloud.google.com/go/spanner/apiv1/spannerpb" + spannerdriver "github.com/googleapis/go-sql-spanner" + "github.com/googleapis/go-sql-spanner/parser" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func CloseConnection(poolId, connId int64) error { + conn, err := findConnection(poolId, connId) + if err != nil { + return err + } + return conn.close() +} + +func WriteMutations(poolId, connId int64, mutations *spannerpb.BatchWriteRequest_MutationGroup) (*spannerpb.CommitResponse, error) { + conn, err := findConnection(poolId, connId) + if err != nil { + return nil, err + } + return conn.writeMutations(mutations) +} + +func BeginTransaction(poolId, connId int64, txOpts *spannerpb.TransactionOptions) error { + conn, err := findConnection(poolId, connId) + if err != nil { + return err + } + return conn.BeginTransaction(txOpts) +} + +func Execute(poolId, connId int64, executeSqlRequest *spannerpb.ExecuteSqlRequest) (int64, error) { + conn, err := findConnection(poolId, connId) + if err != nil { + return 0, err + } + return conn.Execute(executeSqlRequest) +} + +func ExecuteBatch(poolId, connId int64, statements *spannerpb.ExecuteBatchDmlRequest) (*spannerpb.ExecuteBatchDmlResponse, error) { + conn, err := findConnection(poolId, connId) + if err != nil { + return nil, err + } + return conn.ExecuteBatch(statements.Statements) +} + +type Connection struct { + results *sync.Map + resultsIdx atomic.Int64 + + backend *sql.Conn +} + +// spannerConn is an internal interface that contains the internal functions that are used by this API. +type spannerConn interface { + WriteMutations(ctx context.Context, ms []*spanner.Mutation) (*spanner.CommitResponse, error) + BeginReadOnlyTransaction(ctx context.Context, options *spannerdriver.ReadOnlyTransactionOptions) (driver.Tx, error) + BeginReadWriteTransaction(ctx context.Context, options *spannerdriver.ReadWriteTransactionOptions) (driver.Tx, error) + Commit(ctx context.Context) (*spanner.CommitResponse, error) + Rollback(ctx context.Context) error +} + +type queryExecutor interface { + ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) + QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) +} + +func (conn *Connection) close() error { + conn.results.Range(func(key, value interface{}) bool { + res := value.(*rows) + _ = res.Close() + return true + }) + err := conn.backend.Close() + if err != nil { + return err + } + return nil +} + +func (conn *Connection) writeMutations(mutation *spannerpb.BatchWriteRequest_MutationGroup) (*spannerpb.CommitResponse, error) { + ctx := context.Background() + mutations := make([]*spanner.Mutation, 0, len(mutation.Mutations)) + for _, m := range mutation.Mutations { + spannerMutation, err := spanner.WrapMutation(m) + if err != nil { + return nil, err + } + mutations = append(mutations, spannerMutation) + } + var commitResponse *spanner.CommitResponse + if err := conn.backend.Raw(func(driverConn any) (err error) { + sc, _ := driverConn.(spannerConn) + commitResponse, err = sc.WriteMutations(ctx, mutations) + return err + }); err != nil { + return nil, err + } + + // The commit response is nil if the connection is currently in a transaction. + if commitResponse == nil { + return nil, nil + } + response := spannerpb.CommitResponse{ + CommitTimestamp: timestamppb.New(commitResponse.CommitTs), + } + return &response, nil +} + +func (conn *Connection) BeginTransaction(txOpts *spannerpb.TransactionOptions) error { + var err error + ctx := context.Background() + if txOpts.GetReadOnly() != nil { + return conn.beginReadOnlyTransaction(ctx, convertToReadOnlyOpts(txOpts)) + } else if txOpts.GetPartitionedDml() != nil { + err = spanner.ToSpannerError(status.Error(codes.InvalidArgument, "transaction type not supported")) + } else { + return conn.beginReadWriteTransaction(ctx, convertToReadWriteTransactionOptions(txOpts)) + } + if err != nil { + return err + } + return nil +} + +func (conn *Connection) beginReadOnlyTransaction(ctx context.Context, opts *spannerdriver.ReadOnlyTransactionOptions) error { + return conn.backend.Raw(func(driverConn any) (err error) { + sc, _ := driverConn.(spannerConn) + _, err = sc.BeginReadOnlyTransaction(ctx, opts) + return err + }) +} + +func (conn *Connection) beginReadWriteTransaction(ctx context.Context, opts *spannerdriver.ReadWriteTransactionOptions) error { + return conn.backend.Raw(func(driverConn any) (err error) { + sc, _ := driverConn.(spannerConn) + _, err = sc.BeginReadWriteTransaction(ctx, opts) + return err + }) +} + +func convertToReadOnlyOpts(txOpts *spannerpb.TransactionOptions) *spannerdriver.ReadOnlyTransactionOptions { + return &spannerdriver.ReadOnlyTransactionOptions{ + TimestampBound: convertTimestampBound(txOpts), + } +} + +func convertTimestampBound(txOpts *spannerpb.TransactionOptions) spanner.TimestampBound { + ro := txOpts.GetReadOnly() + if ro.GetStrong() { + return spanner.StrongRead() + } else if ro.GetReadTimestamp() != nil { + return spanner.ReadTimestamp(ro.GetReadTimestamp().AsTime()) + } else if ro.GetMinReadTimestamp() != nil { + return spanner.ReadTimestamp(ro.GetMinReadTimestamp().AsTime()) + } else if ro.GetExactStaleness() != nil { + return spanner.ExactStaleness(ro.GetExactStaleness().AsDuration()) + } else if ro.GetMaxStaleness() != nil { + return spanner.MaxStaleness(ro.GetMaxStaleness().AsDuration()) + } + return spanner.TimestampBound{} +} + +func convertToReadWriteTransactionOptions(txOpts *spannerpb.TransactionOptions) *spannerdriver.ReadWriteTransactionOptions { + readLockMode := spannerpb.TransactionOptions_ReadWrite_READ_LOCK_MODE_UNSPECIFIED + if txOpts.GetReadWrite() != nil { + readLockMode = txOpts.GetReadWrite().GetReadLockMode() + } + return &spannerdriver.ReadWriteTransactionOptions{ + TransactionOptions: spanner.TransactionOptions{ + IsolationLevel: txOpts.GetIsolationLevel(), + ReadLockMode: readLockMode, + }, + } +} + +func convertIsolationLevel(level spannerpb.TransactionOptions_IsolationLevel) sql.IsolationLevel { + switch level { + case spannerpb.TransactionOptions_SERIALIZABLE: + return sql.LevelSerializable + case spannerpb.TransactionOptions_REPEATABLE_READ: + return sql.LevelRepeatableRead + } + return sql.LevelDefault +} + +func (conn *Connection) Execute(statement *spannerpb.ExecuteSqlRequest) (int64, error) { + return execute(conn, conn.backend, statement) +} + +func (conn *Connection) ExecuteBatch(statements []*spannerpb.ExecuteBatchDmlRequest_Statement) (*spannerpb.ExecuteBatchDmlResponse, error) { + return executeBatch(conn, conn.backend, statements) +} + +func execute(conn *Connection, executor queryExecutor, statement *spannerpb.ExecuteSqlRequest) (int64, error) { + params := extractParams(statement) + it, err := executor.QueryContext(context.Background(), statement.Sql, params...) + if err != nil { + return 0, err + } + // The first result set should contain the metadata. + if !it.Next() { + return 0, fmt.Errorf("query returned no metadata") + } + metadata := &spannerpb.ResultSetMetadata{} + if err := it.Scan(&metadata); err != nil { + return 0, err + } + // Move to the next result set, which contains the normal data. + if !it.NextResultSet() { + return 0, fmt.Errorf("no results found after metadata") + } + id := conn.resultsIdx.Add(1) + res := &rows{ + backend: it, + metadata: metadata, + } + if len(metadata.RowType.Fields) == 0 { + // No rows returned. Read the stats now. + res.readStats() + } + conn.results.Store(id, res) + return id, nil +} + +func executeBatch(conn *Connection, executor queryExecutor, statements []*spannerpb.ExecuteBatchDmlRequest_Statement) (*spannerpb.ExecuteBatchDmlResponse, error) { + // Determine the type of batch that should be executed based on the type of statements. + batchType, err := determineBatchType(conn, statements) + if err != nil { + return nil, err + } + switch batchType { + case parser.BatchTypeDml: + return executeBatchDml(conn, executor, statements) + case parser.BatchTypeDdl: + return executeBatchDdl(conn, executor, statements) + default: + return nil, status.Errorf(codes.InvalidArgument, "unsupported batch type: %v", batchType) + } +} + +func executeBatchDdl(conn *Connection, executor queryExecutor, statements []*spannerpb.ExecuteBatchDmlRequest_Statement) (*spannerpb.ExecuteBatchDmlResponse, error) { + if err := conn.backend.Raw(func(driverConn any) error { + spannerConn, _ := driverConn.(spannerdriver.SpannerConn) + return spannerConn.StartBatchDDL() + }); err != nil { + return nil, err + } + for _, statement := range statements { + _, err := executor.ExecContext(context.Background(), statement.Sql) + if err != nil { + return nil, err + } + } + // TODO: Add support for getting the actual Batch DDL response. + if err := conn.backend.Raw(func(driverConn any) (err error) { + spannerConn, _ := driverConn.(spannerdriver.SpannerConn) + return spannerConn.RunBatch(context.Background()) + }); err != nil { + return nil, err + } + + response := spannerpb.ExecuteBatchDmlResponse{} + response.ResultSets = make([]*spannerpb.ResultSet, len(statements)) + for i := range statements { + response.ResultSets[i] = &spannerpb.ResultSet{Stats: &spannerpb.ResultSetStats{RowCount: &spannerpb.ResultSetStats_RowCountExact{RowCountExact: 0}}} + } + return &response, nil +} + +func executeBatchDml(conn *Connection, executor queryExecutor, statements []*spannerpb.ExecuteBatchDmlRequest_Statement) (*spannerpb.ExecuteBatchDmlResponse, error) { + if err := conn.backend.Raw(func(driverConn any) error { + spannerConn, _ := driverConn.(spannerdriver.SpannerConn) + return spannerConn.StartBatchDML() + }); err != nil { + return nil, err + } + for _, statement := range statements { + request := &spannerpb.ExecuteSqlRequest{ + Sql: statement.Sql, + Params: statement.Params, + ParamTypes: statement.ParamTypes, + } + params := extractParams(request) + _, err := executor.ExecContext(context.Background(), statement.Sql, params...) + if err != nil { + return nil, err + } + } + var spannerResult spannerdriver.SpannerResult + if err := conn.backend.Raw(func(driverConn any) (err error) { + spannerConn, _ := driverConn.(spannerdriver.SpannerConn) + spannerResult, err = spannerConn.RunDmlBatch(context.Background()) + return err + }); err != nil { + return nil, err + } + affected, err := spannerResult.BatchRowsAffected() + if err != nil { + return nil, err + } + response := spannerpb.ExecuteBatchDmlResponse{} + response.ResultSets = make([]*spannerpb.ResultSet, len(affected)) + for i, aff := range affected { + response.ResultSets[i] = &spannerpb.ResultSet{Stats: &spannerpb.ResultSetStats{RowCount: &spannerpb.ResultSetStats_RowCountExact{RowCountExact: aff}}} + } + return &response, nil +} + +func extractParams(statement *spannerpb.ExecuteSqlRequest) []any { + paramsLen := 1 + if statement.Params != nil { + paramsLen = 1 + len(statement.Params.Fields) + } + params := make([]any, paramsLen) + params = append(params, spannerdriver.ExecOptions{ + DecodeOption: spannerdriver.DecodeOptionProto, + TimestampBound: extractTimestampBound(statement), + ReturnResultSetMetadata: true, + ReturnResultSetStats: true, + DirectExecuteQuery: true, + }) + if statement.Params != nil { + if statement.ParamTypes == nil { + statement.ParamTypes = make(map[string]*spannerpb.Type) + } + for param, value := range statement.Params.Fields { + genericValue := spanner.GenericColumnValue{ + Value: value, + Type: statement.ParamTypes[param], + } + if strings.HasPrefix(param, "_") { + // Prefix the parameter name with a 'p' to work around the fact that database/sql does not allow + // named arguments to start with anything else than a letter. + params = append(params, sql.Named("p"+param, spannerdriver.SpannerNamedArg{NameInQuery: param, Value: genericValue})) + } else { + params = append(params, sql.Named(param, genericValue)) + } + } + } + return params +} + +func determineBatchType(conn *Connection, statements []*spannerpb.ExecuteBatchDmlRequest_Statement) (parser.BatchType, error) { + if len(statements) == 0 { + return parser.BatchTypeDdl, status.Errorf(codes.InvalidArgument, "cannot determine type of an empty batch") + } + var batchType parser.BatchType + if err := conn.backend.Raw(func(driverConn any) error { + spannerConn, _ := driverConn.(spannerdriver.SpannerConn) + firstStatementType := spannerConn.DetectStatementType(statements[0].Sql) + if firstStatementType == parser.StatementTypeDml { + batchType = parser.BatchTypeDml + } else if firstStatementType == parser.StatementTypeDdl { + batchType = parser.BatchTypeDdl + } else { + return status.Errorf(codes.InvalidArgument, "unsupported statement type for batching: %v", firstStatementType) + } + for i, statement := range statements { + if i > 0 { + tp := spannerConn.DetectStatementType(statement.Sql) + if tp != firstStatementType { + return status.Errorf(codes.InvalidArgument, "Batches may not contain different types of statements. The first statement is of type %v. The statement on position %d is of type %v.", firstStatementType, i, tp) + } + } + } + return nil + }); err != nil { + return parser.BatchTypeDdl, err + } + + return batchType, nil +} + +func extractTimestampBound(statement *spannerpb.ExecuteSqlRequest) *spanner.TimestampBound { + if statement.Transaction != nil && statement.Transaction.GetSingleUse() != nil && statement.Transaction.GetSingleUse().GetReadOnly() != nil { + ro := statement.Transaction.GetSingleUse().GetReadOnly() + var t spanner.TimestampBound + if ro.GetStrong() { + t = spanner.StrongRead() + } else if ro.GetMaxStaleness() != nil { + t = spanner.MaxStaleness(ro.GetMaxStaleness().AsDuration()) + } else if ro.GetExactStaleness() != nil { + t = spanner.ExactStaleness(ro.GetExactStaleness().AsDuration()) + } else if ro.GetMinReadTimestamp() != nil { + t = spanner.MinReadTimestamp(ro.GetMinReadTimestamp().AsTime()) + } else if ro.GetReadTimestamp() != nil { + t = spanner.ReadTimestamp(ro.GetReadTimestamp().AsTime()) + } + return &t + } + return nil +} diff --git a/spannerlib/api/pool.go b/spannerlib/api/pool.go new file mode 100644 index 00000000..c41f075b --- /dev/null +++ b/spannerlib/api/pool.go @@ -0,0 +1,105 @@ +package api + +import ( + "context" + "database/sql" + "fmt" + "sync" + "sync/atomic" + + spannerdriver "github.com/googleapis/go-sql-spanner" +) + +var pools = sync.Map{} +var poolsIdx = atomic.Int64{} + +// Pool is the equivalent of a sql.DB. It contains a pool of connections to the same database. +type Pool struct { + db *sql.DB + connections *sync.Map + connectionsIdx atomic.Int64 +} + +func CreatePool(dsn string) (int64, error) { + config, err := spannerdriver.ExtractConnectorConfig(dsn) + if err != nil { + return 0, err + } + connector, err := spannerdriver.CreateConnector(config) + if err != nil { + return 0, err + } + db := sql.OpenDB(connector) + + id := poolsIdx.Add(1) + pool := &Pool{ + db: db, + connections: &sync.Map{}, + } + pools.Store(id, pool) + return id, nil +} + +func ClosePool(id int64) error { + p, ok := pools.LoadAndDelete(id) + if !ok { + return fmt.Errorf("pool %v not found", id) + } + pool := p.(*Pool) + pool.connections.Range(func(key, value interface{}) bool { + conn := value.(*Connection) + _ = conn.close() + return true + }) + if err := pool.db.Close(); err != nil { + return err + } + return nil +} + +func CreateConnection(poolId int64) (int64, error) { + p, ok := pools.Load(poolId) + if !ok { + return 0, fmt.Errorf("pool %v not found", poolId) + } + pool := p.(*Pool) + sqlConn, err := pool.db.Conn(context.Background()) + if err != nil { + return 0, err + } + id := poolsIdx.Add(1) + conn := &Connection{ + backend: sqlConn, + results: &sync.Map{}, + } + pool.connections.Store(id, conn) + + return id, nil +} + +func findConnection(poolId, connId int64) (*Connection, error) { + p, ok := pools.Load(poolId) + if !ok { + return nil, fmt.Errorf("pool %v not found", poolId) + } + pool := p.(*Pool) + c, ok := pool.connections.Load(connId) + if !ok { + return nil, fmt.Errorf("connection %v not found", connId) + } + conn := c.(*Connection) + return conn, nil +} + +func findRows(poolId, connId, rowsId int64) (*rows, error) { + conn, err := findConnection(poolId, connId) + if err != nil { + return nil, err + } + r, ok := conn.results.Load(rowsId) + if !ok { + return nil, fmt.Errorf("rows %v not found", rowsId) + } + res := r.(*rows) + return res, nil +} diff --git a/spannerlib/api/rows.go b/spannerlib/api/rows.go new file mode 100644 index 00000000..18e3efc5 --- /dev/null +++ b/spannerlib/api/rows.go @@ -0,0 +1,181 @@ +package api + +import ( + "database/sql" + "encoding/base64" + "errors" + + "cloud.google.com/go/spanner" + "cloud.google.com/go/spanner/apiv1/spannerpb" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +type EncodeRowOption int32 + +const ( + EncodeRowOptionProto EncodeRowOption = iota +) + +func Metadata(poolId, connId, rowsId int64) (*spannerpb.ResultSetMetadata, error) { + res, err := findRows(poolId, connId, rowsId) + if err != nil { + return nil, err + } + return res.Metadata() +} + +func ResultSetStats(poolId, connId, rowsId int64) (*spannerpb.ResultSetStats, error) { + res, err := findRows(poolId, connId, rowsId) + if err != nil { + return nil, err + } + return res.ResultSetStats() +} + +func NextEncoded(poolId, connId, rowsId int64) ([]byte, error) { + _, bytes, err := next(poolId, connId, rowsId, true) + if err != nil { + return nil, err + } + return bytes, nil +} + +func Next(poolId, connId, rowsId int64) (*structpb.ListValue, error) { + values, _, err := next(poolId, connId, rowsId, false) + if err != nil { + return nil, err + } + return values, nil +} + +func next(poolId, connId, rowsId int64, marshalResult bool) (*structpb.ListValue, []byte, error) { + rows, err := findRows(poolId, connId, rowsId) + if err != nil { + return nil, nil, err + } + values, err := rows.Next() + if err != nil { + return nil, nil, err + } + if !marshalResult || values == nil { + return values, nil, nil + } + + rows.marshalBuffer, err = proto.MarshalOptions{}.MarshalAppend(rows.marshalBuffer[:0], rows.values) + if err != nil { + return nil, nil, err + } + return values, rows.marshalBuffer, nil +} + +func CloseRows(poolId, connId, rowsId int64) error { + res, err := findRows(poolId, connId, rowsId) + if err != nil { + return err + } + conn, err := findConnection(poolId, connId) + if err != nil { + return err + } + conn.results.Delete(rowsId) + return res.Close() +} + +type rows struct { + backend *sql.Rows + metadata *spannerpb.ResultSetMetadata + stats *spannerpb.ResultSetStats + done bool + + buffer []any + values *structpb.ListValue + marshalBuffer []byte +} + +func (rows *rows) Close() error { + err := rows.backend.Close() + if err != nil { + return err + } + return nil +} + +func (rows *rows) metadataBytes() ([]byte, error) { + colTypes, err := rows.backend.ColumnTypes() + if err != nil { + return nil, err + } + return base64.StdEncoding.DecodeString(colTypes[0].DatabaseTypeName()) +} + +func (rows *rows) Metadata() (*spannerpb.ResultSetMetadata, error) { + return rows.metadata, nil +} + +func (rows *rows) ResultSetStats() (*spannerpb.ResultSetStats, error) { + if rows.stats == nil { + rows.readStats() + } + return rows.stats, nil +} + +type genericValue struct { + v *structpb.Value +} + +func (gv *genericValue) Scan(src any) error { + if v, ok := src.(spanner.GenericColumnValue); ok { + gv.v = v.Value + return nil + } + return errors.New("cannot convert value to generic column value") +} + +func (rows *rows) Next() (*structpb.ListValue, error) { + // No columns means no rows, so just return nil to indicate that there are no (more) rows. + if len(rows.metadata.RowType.Fields) == 0 || rows.done { + return nil, nil + } + if rows.stats != nil { + return nil, spanner.ToSpannerError(status.Error(codes.FailedPrecondition, "cannot read more data after returning stats")) + } + ok := rows.backend.Next() + if !ok { + rows.done = true + // No more rows. Read stats and return nil. + rows.readStats() + // nil indicates no more rows. + return nil, nil + } + + if rows.buffer == nil { + rows.buffer = make([]any, len(rows.metadata.RowType.Fields)) + for i := range rows.buffer { + rows.buffer[i] = &genericValue{} + } + rows.values = &structpb.ListValue{ + Values: make([]*structpb.Value, len(rows.buffer)), + } + rows.marshalBuffer = make([]byte, 0) + } + if err := rows.backend.Scan(rows.buffer...); err != nil { + return nil, err + } + for i := range rows.buffer { + rows.values.Values[i] = rows.buffer[i].(*genericValue).v + } + return rows.values, nil +} + +func (rows *rows) readStats() { + rows.stats = &spannerpb.ResultSetStats{} + if !rows.backend.NextResultSet() { + return + } + if rows.backend.Next() { + _ = rows.backend.Scan(&rows.stats) + } +} diff --git a/spannerlib/api/transaction.go b/spannerlib/api/transaction.go new file mode 100644 index 00000000..5d668d29 --- /dev/null +++ b/spannerlib/api/transaction.go @@ -0,0 +1,95 @@ +package api + +import ( + "context" + "database/sql" + + "cloud.google.com/go/spanner" + "cloud.google.com/go/spanner/apiv1/spannerpb" + spannerdriver "github.com/googleapis/go-sql-spanner" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func Commit(poolId, connId int64) (*spannerpb.CommitResponse, error) { + conn, err := findConnection(poolId, connId) + if err != nil { + return nil, err + } + return commit(conn) +} + +func Rollback(poolId, connId int64) error { + conn, err := findConnection(poolId, connId) + if err != nil { + return err + } + return rollback(conn) +} + +type transaction struct { + backend *sql.Tx + conn *Connection + txOpts *spannerpb.TransactionOptions + closed bool +} + +func (tx *transaction) Close() error { + if tx.closed { + return nil + } + tx.closed = true + if err := tx.backend.Rollback(); err != nil { + return err + } + return nil +} + +func (tx *transaction) bufferWrite(mutation *spannerpb.BatchWriteRequest_MutationGroup) error { + mutations := make([]*spanner.Mutation, 0, len(mutation.Mutations)) + for _, m := range mutation.Mutations { + spannerMutation, err := spanner.WrapMutation(m) + if err != nil { + return err + } + mutations = append(mutations, spannerMutation) + } + if err := tx.conn.backend.Raw(func(driverConn any) error { + spannerConn, _ := driverConn.(spannerdriver.SpannerConn) + return spannerConn.BufferWrite(mutations) + }); err != nil { + return err + } + return nil +} + +func (tx *transaction) Execute(statement *spannerpb.ExecuteSqlRequest) (int64, error) { + return execute(tx.conn, tx.backend, statement) +} + +func commit(conn *Connection) (*spannerpb.CommitResponse, error) { + var response *spanner.CommitResponse + if err := conn.backend.Raw(func(driverConn any) (err error) { + spannerConn, _ := driverConn.(spannerConn) + response, err = spannerConn.Commit(context.Background()) + if err != nil { + return err + } + return nil + }); err != nil { + return nil, err + } + + // The commit response is nil for read-only transactions. + if response == nil { + return nil, nil + } + // TODO: Include commit stats + return &spannerpb.CommitResponse{CommitTimestamp: timestamppb.New(response.CommitTs)}, nil +} + +func rollback(conn *Connection) error { + return conn.backend.Raw(func(driverConn any) (err error) { + spannerConn, _ := driverConn.(spannerConn) + return spannerConn.Rollback(context.Background()) + }) +} diff --git a/spannerlib/build.sh b/spannerlib/build.sh new file mode 100755 index 00000000..f9f96578 --- /dev/null +++ b/spannerlib/build.sh @@ -0,0 +1,20 @@ +go build -o shared/spannerlib.so -buildmode=c-shared shared/shared_lib.go +mkdir -p dotnet-spannerlib/Google.Cloud.SpannerLib.Native/libraries/any +cp shared/spannerlib.so dotnet-spannerlib/Google.Cloud.SpannerLib.Native/libraries/any/spannerlib.so + +go build grpc-server/grpc_server.go +mkdir -p dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/binaries/any +cp grpc-server/grpc_server dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/binaries/any/grpc_server + +cd dotnet-spannerlib/Google.Cloud.SpannerLib.Native || exit 1 +dotnet pack +dotnet nuget add source "$PWD"/bin/Release --name local || true + +cd .. || exit 1 +dotnet restore +dotnet build --no-restore + +cd Google.Cloud.SpannerLib.Tests || exit 1 +dotnet test --no-build --verbosity normal + +cd ../.. diff --git a/spannerlib/build_protos.sh b/spannerlib/build_protos.sh new file mode 100644 index 00000000..434fdf2e --- /dev/null +++ b/spannerlib/build_protos.sh @@ -0,0 +1,6 @@ +protoc \ + --go_out=/Users/loite/GolandProjects/go-sql-spanner/spannerlib/ \ + --go_opt=paths=source_relative \ + --go-grpc_out=/Users/loite/GolandProjects/go-sql-spanner/spannerlib/ \ + --go-grpc_opt=paths=source_relative \ + google/spannerlib/v1/spannerlib.proto diff --git a/spannerlib/dotnet-spannerlib/.gitignore b/spannerlib/dotnet-spannerlib/.gitignore new file mode 100644 index 00000000..fd9decc3 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/.gitignore @@ -0,0 +1,34 @@ +bin/ +obj/ +packages/ +*.suo +.nuget/ +TestResults/ +*TestResults.xml +*sponge_log.xml +*.csproj.user +.idea/ +*.iml +PublishProfiles/ +*.sln.DotSettings.user +.vs/ +*~ +*.xproj.user +project.lock.json +.vscode/ +secrets.ps1 +google_application_credentials.json +**/Properties/launchSettings.json +**/wwwroot/lib/ +**/kmsCipher.key +/tmpB19A.tmp +/dlp/tmp6849.tmp +env/ +applications/liftAndShiftPetshop/PrecompiledWeb/ +applications/liftAndShiftPetshop/Web/ +*.leu +.cr/ +BenchmarkDotNet.Artifacts +.DS_Store + +!spannerlib.so diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/.gitignore b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/.gitignore new file mode 100644 index 00000000..e935fd38 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/.gitignore @@ -0,0 +1 @@ +binaries diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/Google.Cloud.SpannerLib.Grpc.csproj b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/Google.Cloud.SpannerLib.Grpc.csproj new file mode 100644 index 00000000..562cb07d --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/Google.Cloud.SpannerLib.Grpc.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + enable + default + Experimental.SpannerLib.Grpc + Experimental gRPC library for Spanner + 1.0.3 + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/SpannerLib.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/SpannerLib.cs new file mode 100644 index 00000000..72bb7932 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/SpannerLib.cs @@ -0,0 +1,266 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Threading; +using Google.Cloud.Spanner.V1; +using Google.Cloud.SpannerLib.V1; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; +using Grpc.Net.Client; +using BeginTransactionRequest = Google.Cloud.SpannerLib.V1.BeginTransactionRequest; +using ExecuteBatchDmlRequest = Google.Cloud.Spanner.V1.ExecuteBatchDmlRequest; +using Transaction = Google.Cloud.SpannerLib.V1.Transaction; + +namespace Google.Cloud.SpannerLib.Grpc +{ + public class SpannerLib : IDisposable + { + private readonly Process _process; + private readonly string _fileName; + private readonly V1.SpannerLib.SpannerLibClient _client; + private readonly GrpcChannel _channel; + + private bool _disposed; + + public SpannerLib() + { + (_fileName, _process) = StartGrpcServer(); + (_client, _channel) = CreateClient(_fileName); + } + + ~SpannerLib() + { + Dispose(false); + } + + private static string GetBinaryFileName() + { + if (File.Exists("runtimes/any/native/grpc_server")) + { + return "runtimes/any/native/grpc_server"; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + switch (RuntimeInformation.OSArchitecture) + { + case Architecture.X64: + return "runtimes/linux-x64/native/grpc_server"; + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.Arm64: + return "runtimes/osx-arm64/native/grpc_server"; + } + } + throw new PlatformNotSupportedException(); + } + + private static Tuple StartGrpcServer() + { + // Generate a random temp file name that will be used for the Unix domain socket communication. + var unixDomainSocketFileName = Path.GetTempPath() + Guid.NewGuid(); + var binaryFileName = GetBinaryFileName(); + var info = new ProcessStartInfo + { + Arguments = unixDomainSocketFileName, + UseShellExecute = false, + FileName = binaryFileName, + RedirectStandardOutput = true, + }; + // Start the process as a child process. The process will automatically stop when the + // parent process stops. + var process = Process.Start(info); + if (process == null) + { + throw new InvalidOperationException("Failed to start spanner"); + } + while (!File.Exists(unixDomainSocketFileName)) + { + Thread.Sleep(1); + } + // Return the name of the Unix domain socket. + return Tuple.Create(unixDomainSocketFileName, process); + } + + private static Tuple CreateClient(string file) + { + var channel = ForUnixSocket(file); + return Tuple.Create(new V1.SpannerLib.SpannerLibClient(channel), channel); + } + + private static GrpcChannel ForUnixSocket(string fileName) + { + var endpoint = new UnixDomainSocketEndPoint(fileName); + return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions { + HttpHandler = new SocketsHttpHandler { + EnableMultipleHttp2Connections = true, + ConnectCallback = async (_, cancellationToken) => { + var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + try { + await socket.ConnectAsync(endpoint, cancellationToken).ConfigureAwait(false); + return new NetworkStream(socket, true); + } catch { + socket.Dispose(); + throw; + } + } + } + }); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Close() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + try + { + _channel.Dispose(); + _process.Dispose(); + } + finally + { + _disposed = true; + } + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + public Pool CreatePool(string dsn) + { + CheckDisposed(); + return _client.CreatePool(new CreatePoolRequest { Dsn = dsn }); + } + + public void ClosePool(Pool pool) + { + CheckDisposed(); + _client.ClosePool(pool); + } + + public Connection CreateConnection(Pool pool) + { + CheckDisposed(); + return _client.CreateConnection(new CreateConnectionRequest { Pool = pool }); + } + + public void CloseConnection(Connection connection) + { + CheckDisposed(); + _client.CloseConnection(connection); + } + + public CommitResponse Apply(Connection connection, BatchWriteRequest.Types.MutationGroup mutations) + { + CheckDisposed(); + return _client.Apply(new ApplyRequest { Connection = connection, Mutations = mutations }); + } + + public void BufferWrite(Transaction transaction, BatchWriteRequest.Types.MutationGroup mutations) + { + CheckDisposed(); + _client.BufferWrite(new BufferWriteRequest { Transaction = transaction, Mutations = mutations }); + } + + public Rows Execute(Connection connection, ExecuteSqlRequest statement) + { + CheckDisposed(); + return _client.Execute(new ExecuteRequest { Connection = connection, ExecuteSqlRequest = statement }); + } + + public AsyncServerStreamingCall ExecuteStreaming(Connection connection, ExecuteSqlRequest statement) + { + CheckDisposed(); + return _client.ExecuteStreaming(new ExecuteRequest { Connection = connection, ExecuteSqlRequest = statement }); + } + + public Rows ExecuteTransaction(Transaction transaction, ExecuteSqlRequest statement) + { + CheckDisposed(); + return _client.ExecuteTransaction(new ExecuteTransactionRequest { Transaction = transaction, ExecuteSqlRequest = statement }); + } + + public ExecuteBatchDmlResponse ExecuteBatchDml(Connection connection, ExecuteBatchDmlRequest statements) + { + CheckDisposed(); + return _client.ExecuteBatchDml(new V1.ExecuteBatchDmlRequest { Connection = connection, ExecuteBatchDmlRequest_ = statements }); + } + + public ResultSetMetadata Metadata(Rows rows) + { + CheckDisposed(); + return _client.Metadata(rows); + } + + public ListValue Next(Rows rows) + { + CheckDisposed(); + return _client.Next(rows); + } + + public ResultSetStats ResultSetStats(Rows rows) + { + CheckDisposed(); + return _client.ResultSetStats(rows); + } + + public void CloseRows(Rows rows) + { + CheckDisposed(); + _client.CloseRows(rows); + } + + public Transaction BeginTransaction(Connection connection, TransactionOptions transactionOptions) + { + CheckDisposed(); + return _client.BeginTransaction(new BeginTransactionRequest + { Connection = connection, TransactionOptions = transactionOptions }); + } + + public CommitResponse Commit(Transaction transaction) + { + CheckDisposed(); + return _client.Commit(transaction); + } + + public void Rollback(Transaction transaction) + { + CheckDisposed(); + _client.Rollback(transaction); + } + + public AsyncDuplexStreamingCall CreateStream() + { + CheckDisposed(); + return _client.ConnectionStream(); + } + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/annotations.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/annotations.proto new file mode 100644 index 00000000..417edd8f --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/client.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/client.proto new file mode 100644 index 00000000..3d692560 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/client.proto @@ -0,0 +1,486 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/launch_stage.proto"; +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ClientProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // A definition of a client library method signature. + // + // In client libraries, each proto RPC corresponds to one or more methods + // which the end user is able to call, and calls the underlying RPC. + // Normally, this method receives a single argument (a struct or instance + // corresponding to the RPC request object). Defining this field will + // add one or more overloads providing flattened or simpler method signatures + // in some languages. + // + // The fields on the method signature are provided as a comma-separated + // string. + // + // For example, the proto RPC and annotation: + // + // rpc CreateSubscription(CreateSubscriptionRequest) + // returns (Subscription) { + // option (google.api.method_signature) = "name,topic"; + // } + // + // Would add the following Java overload (in addition to the method accepting + // the request object): + // + // public final Subscription createSubscription(String name, String topic) + // + // The following backwards-compatibility guidelines apply: + // + // * Adding this annotation to an unannotated method is backwards + // compatible. + // * Adding this annotation to a method which already has existing + // method signature annotations is backwards compatible if and only if + // the new method signature annotation is last in the sequence. + // * Modifying or removing an existing method signature annotation is + // a breaking change. + // * Re-ordering existing method signature annotations is a breaking + // change. + repeated string method_signature = 1051; +} + +extend google.protobuf.ServiceOptions { + // The hostname for this service. + // This should be specified with no prefix or protocol. + // + // Example: + // + // service Foo { + // option (google.api.default_host) = "foo.googleapi.com"; + // ... + // } + string default_host = 1049; + + // OAuth scopes needed for the client. + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform"; + // ... + // } + // + // If there is more than one scope, use a comma-separated string: + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform," + // "https://www.googleapis.com/auth/monitoring"; + // ... + // } + string oauth_scopes = 1050; + + // The API version of this service, which should be sent by version-aware + // clients to the service. This allows services to abide by the schema and + // behavior of the service at the time this API version was deployed. + // The format of the API version must be treated as opaque by clients. + // Services may use a format with an apparent structure, but clients must + // not rely on this to determine components within an API version, or attempt + // to construct other valid API versions. Note that this is for upcoming + // functionality and may not be implemented for all services. + // + // Example: + // + // service Foo { + // option (google.api.api_version) = "v1_20230821_preview"; + // } + string api_version = 525000001; +} + +// Required information for every language. +message CommonLanguageSettings { + // Link to automatically generated reference documentation. Example: + // https://cloud.google.com/nodejs/docs/reference/asset/latest + string reference_docs_uri = 1 [deprecated = true]; + + // The destination where API teams want this client library to be published. + repeated ClientLibraryDestination destinations = 2; + + // Configuration for which RPCs should be generated in the GAPIC client. + SelectiveGapicGeneration selective_gapic_generation = 3; +} + +// Details about how and where to publish client libraries. +message ClientLibrarySettings { + // Version of the API to apply these settings to. This is the full protobuf + // package for the API, ending in the version element. + // Examples: "google.cloud.speech.v1" and "google.spanner.admin.database.v1". + string version = 1; + + // Launch stage of this version of the API. + LaunchStage launch_stage = 2; + + // When using transport=rest, the client request will encode enums as + // numbers rather than strings. + bool rest_numeric_enums = 3; + + // Settings for legacy Java features, supported in the Service YAML. + JavaSettings java_settings = 21; + + // Settings for C++ client libraries. + CppSettings cpp_settings = 22; + + // Settings for PHP client libraries. + PhpSettings php_settings = 23; + + // Settings for Python client libraries. + PythonSettings python_settings = 24; + + // Settings for Node client libraries. + NodeSettings node_settings = 25; + + // Settings for .NET client libraries. + DotnetSettings dotnet_settings = 26; + + // Settings for Ruby client libraries. + RubySettings ruby_settings = 27; + + // Settings for Go client libraries. + GoSettings go_settings = 28; +} + +// This message configures the settings for publishing [Google Cloud Client +// libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) +// generated from the service config. +message Publishing { + // A list of API method settings, e.g. the behavior for methods that use the + // long-running operation pattern. + repeated MethodSettings method_settings = 2; + + // Link to a *public* URI where users can report issues. Example: + // https://issuetracker.google.com/issues/new?component=190865&template=1161103 + string new_issue_uri = 101; + + // Link to product home page. Example: + // https://cloud.google.com/asset-inventory/docs/overview + string documentation_uri = 102; + + // Used as a tracking tag when collecting data about the APIs developer + // relations artifacts like docs, packages delivered to package managers, + // etc. Example: "speech". + string api_short_name = 103; + + // GitHub label to apply to issues and pull requests opened for this API. + string github_label = 104; + + // GitHub teams to be added to CODEOWNERS in the directory in GitHub + // containing source code for the client libraries for this API. + repeated string codeowner_github_teams = 105; + + // A prefix used in sample code when demarking regions to be included in + // documentation. + string doc_tag_prefix = 106; + + // For whom the client library is being published. + ClientLibraryOrganization organization = 107; + + // Client library settings. If the same version string appears multiple + // times in this list, then the last one wins. Settings from earlier + // settings with the same version string are discarded. + repeated ClientLibrarySettings library_settings = 109; + + // Optional link to proto reference documentation. Example: + // https://cloud.google.com/pubsub/lite/docs/reference/rpc + string proto_reference_documentation_uri = 110; + + // Optional link to REST reference documentation. Example: + // https://cloud.google.com/pubsub/lite/docs/reference/rest + string rest_reference_documentation_uri = 111; +} + +// Settings for Java client libraries. +message JavaSettings { + // The package name to use in Java. Clobbers the java_package option + // set in the protobuf. This should be used **only** by APIs + // who have already set the language_settings.java.package_name" field + // in gapic.yaml. API teams should use the protobuf java_package option + // where possible. + // + // Example of a YAML configuration:: + // + // publishing: + // java_settings: + // library_package: com.google.cloud.pubsub.v1 + string library_package = 1; + + // Configure the Java class name to use instead of the service's for its + // corresponding generated GAPIC client. Keys are fully-qualified + // service names as they appear in the protobuf (including the full + // the language_settings.java.interface_names" field in gapic.yaml. API + // teams should otherwise use the service name as it appears in the + // protobuf. + // + // Example of a YAML configuration:: + // + // publishing: + // java_settings: + // service_class_names: + // - google.pubsub.v1.Publisher: TopicAdmin + // - google.pubsub.v1.Subscriber: SubscriptionAdmin + map service_class_names = 2; + + // Some settings. + CommonLanguageSettings common = 3; +} + +// Settings for C++ client libraries. +message CppSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Php client libraries. +message PhpSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Python client libraries. +message PythonSettings { + // Experimental features to be included during client library generation. + // These fields will be deprecated once the feature graduates and is enabled + // by default. + message ExperimentalFeatures { + // Enables generation of asynchronous REST clients if `rest` transport is + // enabled. By default, asynchronous REST clients will not be generated. + // This feature will be enabled by default 1 month after launching the + // feature in preview packages. + bool rest_async_io_enabled = 1; + + // Enables generation of protobuf code using new types that are more + // Pythonic which are included in `protobuf>=5.29.x`. This feature will be + // enabled by default 1 month after launching the feature in preview + // packages. + bool protobuf_pythonic_types_enabled = 2; + + // Disables generation of an unversioned Python package for this client + // library. This means that the module names will need to be versioned in + // import statements. For example `import google.cloud.library_v2` instead + // of `import google.cloud.library`. + bool unversioned_package_disabled = 3; + } + + // Some settings. + CommonLanguageSettings common = 1; + + // Experimental features to be included during client library generation. + ExperimentalFeatures experimental_features = 2; +} + +// Settings for Node client libraries. +message NodeSettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Dotnet client libraries. +message DotnetSettings { + // Some settings. + CommonLanguageSettings common = 1; + + // Map from original service names to renamed versions. + // This is used when the default generated types + // would cause a naming conflict. (Neither name is + // fully-qualified.) + // Example: Subscriber to SubscriberServiceApi. + map renamed_services = 2; + + // Map from full resource types to the effective short name + // for the resource. This is used when otherwise resource + // named from different services would cause naming collisions. + // Example entry: + // "datalabeling.googleapis.com/Dataset": "DataLabelingDataset" + map renamed_resources = 3; + + // List of full resource types to ignore during generation. + // This is typically used for API-specific Location resources, + // which should be handled by the generator as if they were actually + // the common Location resources. + // Example entry: "documentai.googleapis.com/Location" + repeated string ignored_resources = 4; + + // Namespaces which must be aliased in snippets due to + // a known (but non-generator-predictable) naming collision + repeated string forced_namespace_aliases = 5; + + // Method signatures (in the form "service.method(signature)") + // which are provided separately, so shouldn't be generated. + // Snippets *calling* these methods are still generated, however. + repeated string handwritten_signatures = 6; +} + +// Settings for Ruby client libraries. +message RubySettings { + // Some settings. + CommonLanguageSettings common = 1; +} + +// Settings for Go client libraries. +message GoSettings { + // Some settings. + CommonLanguageSettings common = 1; + + // Map of service names to renamed services. Keys are the package relative + // service names and values are the name to be used for the service client + // and call options. + // + // publishing: + // go_settings: + // renamed_services: + // Publisher: TopicAdmin + map renamed_services = 2; +} + +// Describes the generator configuration for a method. +message MethodSettings { + // Describes settings to use when generating API methods that use the + // long-running operation pattern. + // All default values below are from those used in the client library + // generators (e.g. + // [Java](https://github.com/googleapis/gapic-generator-java/blob/04c2faa191a9b5a10b92392fe8482279c4404803/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java)). + message LongRunning { + // Initial delay after which the first poll request will be made. + // Default value: 5 seconds. + google.protobuf.Duration initial_poll_delay = 1; + + // Multiplier to gradually increase delay between subsequent polls until it + // reaches max_poll_delay. + // Default value: 1.5. + float poll_delay_multiplier = 2; + + // Maximum time between two subsequent poll requests. + // Default value: 45 seconds. + google.protobuf.Duration max_poll_delay = 3; + + // Total polling timeout. + // Default value: 5 minutes. + google.protobuf.Duration total_poll_timeout = 4; + } + + // The fully qualified name of the method, for which the options below apply. + // This is used to find the method to apply the options. + // + // Example: + // + // publishing: + // method_settings: + // - selector: google.storage.control.v2.StorageControl.CreateFolder + // # method settings for CreateFolder... + string selector = 1; + + // Describes settings to use for long-running operations when generating + // API methods for RPCs. Complements RPCs that use the annotations in + // google/longrunning/operations.proto. + // + // Example of a YAML configuration:: + // + // publishing: + // method_settings: + // - selector: google.cloud.speech.v2.Speech.BatchRecognize + // long_running: + // initial_poll_delay: 60s # 1 minute + // poll_delay_multiplier: 1.5 + // max_poll_delay: 360s # 6 minutes + // total_poll_timeout: 54000s # 90 minutes + LongRunning long_running = 2; + + // List of top-level fields of the request message, that should be + // automatically populated by the client libraries based on their + // (google.api.field_info).format. Currently supported format: UUID4. + // + // Example of a YAML configuration: + // + // publishing: + // method_settings: + // - selector: google.example.v1.ExampleService.CreateExample + // auto_populated_fields: + // - request_id + repeated string auto_populated_fields = 3; +} + +// The organization for which the client libraries are being published. +// Affects the url where generated docs are published, etc. +enum ClientLibraryOrganization { + // Not useful. + CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED = 0; + + // Google Cloud Platform Org. + CLOUD = 1; + + // Ads (Advertising) Org. + ADS = 2; + + // Photos Org. + PHOTOS = 3; + + // Street View Org. + STREET_VIEW = 4; + + // Shopping Org. + SHOPPING = 5; + + // Geo Org. + GEO = 6; + + // Generative AI - https://developers.generativeai.google + GENERATIVE_AI = 7; +} + +// To where should client libraries be published? +enum ClientLibraryDestination { + // Client libraries will neither be generated nor published to package + // managers. + CLIENT_LIBRARY_DESTINATION_UNSPECIFIED = 0; + + // Generate the client library in a repo under github.com/googleapis, + // but don't publish it to package managers. + GITHUB = 10; + + // Publish the library to package managers like nuget.org and npmjs.com. + PACKAGE_MANAGER = 20; +} + +// This message is used to configure the generation of a subset of the RPCs in +// a service for client libraries. +message SelectiveGapicGeneration { + // An allowlist of the fully qualified names of RPCs that should be included + // on public client surfaces. + repeated string methods = 1; + + // Setting this to true indicates to the client generators that methods + // that would be excluded from the generation should instead be generated + // in a way that indicates these methods should not be consumed by + // end users. How this is expressed is up to individual language + // implementations to decide. Some examples may be: added annotations, + // obfuscated identifiers, or other language idiomatic patterns. + bool generate_omitted_as_internal = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/field_behavior.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/field_behavior.proto new file mode 100644 index 00000000..1fdaaed1 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/field_behavior.proto @@ -0,0 +1,104 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "FieldBehaviorProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // A designation of a specific field behavior (required, output only, etc.) + // in protobuf messages. + // + // Examples: + // + // string name = 1 [(google.api.field_behavior) = REQUIRED]; + // State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + // google.protobuf.Duration ttl = 1 + // [(google.api.field_behavior) = INPUT_ONLY]; + // google.protobuf.Timestamp expire_time = 1 + // [(google.api.field_behavior) = OUTPUT_ONLY, + // (google.api.field_behavior) = IMMUTABLE]; + repeated google.api.FieldBehavior field_behavior = 1052 [packed = false]; +} + +// An indicator of the behavior of a given field (for example, that a field +// is required in requests, or given as output but ignored as input). +// This **does not** change the behavior in protocol buffers itself; it only +// denotes the behavior and may affect how API tooling handles the field. +// +// Note: This enum **may** receive new values in the future. +enum FieldBehavior { + // Conventional default for enums. Do not use this. + FIELD_BEHAVIOR_UNSPECIFIED = 0; + + // Specifically denotes a field as optional. + // While all fields in protocol buffers are optional, this may be specified + // for emphasis if appropriate. + OPTIONAL = 1; + + // Denotes a field as required. + // This indicates that the field **must** be provided as part of the request, + // and failure to do so will cause an error (usually `INVALID_ARGUMENT`). + REQUIRED = 2; + + // Denotes a field as output only. + // This indicates that the field is provided in responses, but including the + // field in a request does nothing (the server *must* ignore it and + // *must not* throw an error as a result of the field's presence). + OUTPUT_ONLY = 3; + + // Denotes a field as input only. + // This indicates that the field is provided in requests, and the + // corresponding field is not included in output. + INPUT_ONLY = 4; + + // Denotes a field as immutable. + // This indicates that the field may be set once in a request to create a + // resource, but may not be changed thereafter. + IMMUTABLE = 5; + + // Denotes that a (repeated) field is an unordered list. + // This indicates that the service may provide the elements of the list + // in any arbitrary order, rather than the order the user originally + // provided. Additionally, the list's order may or may not be stable. + UNORDERED_LIST = 6; + + // Denotes that this field returns a non-empty default value if not set. + // This indicates that if the user provides the empty value in a request, + // a non-empty value will be returned. The user will not be aware of what + // non-empty value to expect. + NON_EMPTY_DEFAULT = 7; + + // Denotes that the field in a resource (a message annotated with + // google.api.resource) is used in the resource name to uniquely identify the + // resource. For AIP-compliant APIs, this should only be applied to the + // `name` field on the resource. + // + // This behavior should not be applied to references to other resources within + // the message. + // + // The identifier field of resources often have different field behavior + // depending on the request it is embedded in (e.g. for Create methods name + // is optional and unused, while for Update methods it is required). Instead + // of method-specific annotations, only `IDENTIFIER` is required. + IDENTIFIER = 8; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/http.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/http.proto new file mode 100644 index 00000000..57621b53 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/http.proto @@ -0,0 +1,370 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// - HTTP: `GET /v1/messages/123456` +// - gRPC: `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo` +// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub: +// SubMessage(subfield: "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` +// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` +// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// - HTTP: `GET /v1/messages/123456` +// - gRPC: `GetMessage(message_id: "123456")` +// +// - HTTP: `GET /v1/users/me/messages/123456` +// - gRPC: `GetMessage(user_id: "me" message_id: "123456")` +// +// Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// The following example selects a gRPC method and applies an `HttpRule` to it: +// +// http: +// rules: +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/launch_stage.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/launch_stage.proto new file mode 100644 index 00000000..1e86c1ad --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/launch_stage.proto @@ -0,0 +1,72 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api;api"; +option java_multiple_files = true; +option java_outer_classname = "LaunchStageProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// The launch stage as defined by [Google Cloud Platform +// Launch Stages](https://cloud.google.com/terms/launch-stages). +enum LaunchStage { + // Do not use this default value. + LAUNCH_STAGE_UNSPECIFIED = 0; + + // The feature is not yet implemented. Users can not use it. + UNIMPLEMENTED = 6; + + // Prelaunch features are hidden from users and are only visible internally. + PRELAUNCH = 7; + + // Early Access features are limited to a closed group of testers. To use + // these features, you must sign up in advance and sign a Trusted Tester + // agreement (which includes confidentiality provisions). These features may + // be unstable, changed in backward-incompatible ways, and are not + // guaranteed to be released. + EARLY_ACCESS = 1; + + // Alpha is a limited availability test for releases before they are cleared + // for widespread use. By Alpha, all significant design issues are resolved + // and we are in the process of verifying functionality. Alpha customers + // need to apply for access, agree to applicable terms, and have their + // projects allowlisted. Alpha releases don't have to be feature complete, + // no SLAs are provided, and there are no technical support obligations, but + // they will be far enough along that customers can actually use them in + // test environments or for limited-use tests -- just like they would in + // normal production cases. + ALPHA = 2; + + // Beta is the point at which we are ready to open a release for any + // customer to use. There are no SLA or technical support obligations in a + // Beta release. Products will be complete from a feature perspective, but + // may have some open outstanding issues. Beta releases are suitable for + // limited production use cases. + BETA = 3; + + // GA features are open to all developers and are considered stable and + // fully qualified for production use. + GA = 4; + + // Deprecated features are scheduled to be shut down and removed. For more + // information, see the "Deprecation Policy" section of our [Terms of + // Service](https://cloud.google.com/terms/) + // and the [Google Cloud Platform Subject to the Deprecation + // Policy](https://cloud.google.com/terms/deprecation) documentation. + DEPRECATED = 5; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/resource.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/resource.proto new file mode 100644 index 00000000..5669cbc9 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/api/resource.proto @@ -0,0 +1,242 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ResourceProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // An annotation that describes a resource reference, see + // [ResourceReference][]. + google.api.ResourceReference resource_reference = 1055; +} + +extend google.protobuf.FileOptions { + // An annotation that describes a resource definition without a corresponding + // message; see [ResourceDescriptor][]. + repeated google.api.ResourceDescriptor resource_definition = 1053; +} + +extend google.protobuf.MessageOptions { + // An annotation that describes a resource definition, see + // [ResourceDescriptor][]. + google.api.ResourceDescriptor resource = 1053; +} + +// A simple descriptor of a resource type. +// +// ResourceDescriptor annotates a resource message (either by means of a +// protobuf annotation or use in the service config), and associates the +// resource's schema, the resource type, and the pattern of the resource name. +// +// Example: +// +// message Topic { +// // Indicates this message defines a resource schema. +// // Declares the resource type in the format of {service}/{kind}. +// // For Kubernetes resources, the format is {api group}/{kind}. +// option (google.api.resource) = { +// type: "pubsub.googleapis.com/Topic" +// pattern: "projects/{project}/topics/{topic}" +// }; +// } +// +// The ResourceDescriptor Yaml config will look like: +// +// resources: +// - type: "pubsub.googleapis.com/Topic" +// pattern: "projects/{project}/topics/{topic}" +// +// Sometimes, resources have multiple patterns, typically because they can +// live under multiple parents. +// +// Example: +// +// message LogEntry { +// option (google.api.resource) = { +// type: "logging.googleapis.com/LogEntry" +// pattern: "projects/{project}/logs/{log}" +// pattern: "folders/{folder}/logs/{log}" +// pattern: "organizations/{organization}/logs/{log}" +// pattern: "billingAccounts/{billing_account}/logs/{log}" +// }; +// } +// +// The ResourceDescriptor Yaml config will look like: +// +// resources: +// - type: 'logging.googleapis.com/LogEntry' +// pattern: "projects/{project}/logs/{log}" +// pattern: "folders/{folder}/logs/{log}" +// pattern: "organizations/{organization}/logs/{log}" +// pattern: "billingAccounts/{billing_account}/logs/{log}" +message ResourceDescriptor { + // A description of the historical or future-looking state of the + // resource pattern. + enum History { + // The "unset" value. + HISTORY_UNSPECIFIED = 0; + + // The resource originally had one pattern and launched as such, and + // additional patterns were added later. + ORIGINALLY_SINGLE_PATTERN = 1; + + // The resource has one pattern, but the API owner expects to add more + // later. (This is the inverse of ORIGINALLY_SINGLE_PATTERN, and prevents + // that from being necessary once there are multiple patterns.) + FUTURE_MULTI_PATTERN = 2; + } + + // A flag representing a specific style that a resource claims to conform to. + enum Style { + // The unspecified value. Do not use. + STYLE_UNSPECIFIED = 0; + + // This resource is intended to be "declarative-friendly". + // + // Declarative-friendly resources must be more strictly consistent, and + // setting this to true communicates to tools that this resource should + // adhere to declarative-friendly expectations. + // + // Note: This is used by the API linter (linter.aip.dev) to enable + // additional checks. + DECLARATIVE_FRIENDLY = 1; + } + + // The resource type. It must be in the format of + // {service_name}/{resource_type_kind}. The `resource_type_kind` must be + // singular and must not include version numbers. + // + // Example: `storage.googleapis.com/Bucket` + // + // The value of the resource_type_kind must follow the regular expression + // /[A-Za-z][a-zA-Z0-9]+/. It should start with an upper case character and + // should use PascalCase (UpperCamelCase). The maximum number of + // characters allowed for the `resource_type_kind` is 100. + string type = 1; + + // Optional. The relative resource name pattern associated with this resource + // type. The DNS prefix of the full resource name shouldn't be specified here. + // + // The path pattern must follow the syntax, which aligns with HTTP binding + // syntax: + // + // Template = Segment { "/" Segment } ; + // Segment = LITERAL | Variable ; + // Variable = "{" LITERAL "}" ; + // + // Examples: + // + // - "projects/{project}/topics/{topic}" + // - "projects/{project}/knowledgeBases/{knowledge_base}" + // + // The components in braces correspond to the IDs for each resource in the + // hierarchy. It is expected that, if multiple patterns are provided, + // the same component name (e.g. "project") refers to IDs of the same + // type of resource. + repeated string pattern = 2; + + // Optional. The field on the resource that designates the resource name + // field. If omitted, this is assumed to be "name". + string name_field = 3; + + // Optional. The historical or future-looking state of the resource pattern. + // + // Example: + // + // // The InspectTemplate message originally only supported resource + // // names with organization, and project was added later. + // message InspectTemplate { + // option (google.api.resource) = { + // type: "dlp.googleapis.com/InspectTemplate" + // pattern: + // "organizations/{organization}/inspectTemplates/{inspect_template}" + // pattern: "projects/{project}/inspectTemplates/{inspect_template}" + // history: ORIGINALLY_SINGLE_PATTERN + // }; + // } + History history = 4; + + // The plural name used in the resource name and permission names, such as + // 'projects' for the resource name of 'projects/{project}' and the permission + // name of 'cloudresourcemanager.googleapis.com/projects.get'. One exception + // to this is for Nested Collections that have stuttering names, as defined + // in [AIP-122](https://google.aip.dev/122#nested-collections), where the + // collection ID in the resource name pattern does not necessarily directly + // match the `plural` value. + // + // It is the same concept of the `plural` field in k8s CRD spec + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ + // + // Note: The plural form is required even for singleton resources. See + // https://aip.dev/156 + string plural = 5; + + // The same concept of the `singular` field in k8s CRD spec + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ + // Such as "project" for the `resourcemanager.googleapis.com/Project` type. + string singular = 6; + + // Style flag(s) for this resource. + // These indicate that a resource is expected to conform to a given + // style. See the specific style flags for additional information. + repeated Style style = 10; +} + +// Defines a proto annotation that describes a string field that refers to +// an API resource. +message ResourceReference { + // The resource type that the annotated field references. + // + // Example: + // + // message Subscription { + // string topic = 2 [(google.api.resource_reference) = { + // type: "pubsub.googleapis.com/Topic" + // }]; + // } + // + // Occasionally, a field may reference an arbitrary resource. In this case, + // APIs use the special value * in their resource reference. + // + // Example: + // + // message GetIamPolicyRequest { + // string resource = 2 [(google.api.resource_reference) = { + // type: "*" + // }]; + // } + string type = 1; + + // The resource type of a child collection that the annotated field + // references. This is useful for annotating the `parent` field that + // doesn't have a fixed resource type. + // + // Example: + // + // message ListLogEntriesRequest { + // string parent = 1 [(google.api.resource_reference) = { + // child_type: "logging.googleapis.com/LogEntry" + // }; + // } + string child_type = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/BUILD.bazel new file mode 100644 index 00000000..31fd6457 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/BUILD.bazel @@ -0,0 +1,167 @@ +############################################################################## +# Java +############################################################################## +load( + "@com_google_googleapis_imports//:imports.bzl", + "cc_proto_library", + "go_proto_library", + "java_gapic_assembly_gradle_pkg", + "java_proto_library", + "php_gapic_assembly_pkg", + "php_proto_library", + "py_gapic_assembly_pkg", + "py_proto_library", + "upb_c_proto_library", +) +load("@rules_proto//proto:defs.bzl", "proto_library") + +# This is an API workspace, having public visibility by default makes perfect sense. +package(default_visibility = ["//visibility:public"]) + +############################################################################## +# Common +############################################################################## +proto_library( + name = "code_proto", + srcs = ["code.proto"], + deps = [], +) + +proto_library( + name = "error_details_proto", + srcs = ["error_details.proto"], + deps = [ + "@com_google_protobuf//:duration_proto", + ], +) + +proto_library( + name = "http_proto", + srcs = ["http.proto"], +) + +proto_library( + name = "status_proto", + srcs = ["status.proto"], + deps = [ + "@com_google_protobuf//:any_proto", + ], +) + +java_proto_library( + name = "rpc_java_proto", + deps = [ + ":code_proto", + ":error_details_proto", + ":status_proto", + ], +) + +# Please DO-NOT-REMOVE this section. +# This is required to generate java files for these protos. +# Open Source Packages +java_gapic_assembly_gradle_pkg( + name = "google-rpc-java", + transport = "grpc+rest", + deps = [ + "error_details_proto", + "status_proto", + ":code_proto", + ":rpc_java_proto", + ], +) + +go_proto_library( + name = "code_go_proto", + importpath = "google.golang.org/genproto/googleapis/rpc/code", + protos = [":code_proto"], +) + +go_proto_library( + name = "errdetails_go_proto", + importpath = "google.golang.org/genproto/googleapis/rpc/errdetails", + protos = [":error_details_proto"], +) + +go_proto_library( + name = "status_go_proto", + importpath = "google.golang.org/genproto/googleapis/rpc/status", + protos = [":status_proto"], +) + +cc_proto_library( + name = "code_cc_proto", + deps = [":code_proto"], +) + +cc_proto_library( + name = "error_details_cc_proto", + deps = [":error_details_proto"], +) + +cc_proto_library( + name = "status_cc_proto", + deps = [":status_proto"], +) + +upb_c_proto_library( + name = "code_upb_proto", + deps = [":code_proto"], +) + +upb_c_proto_library( + name = "status_upb_proto", + deps = [":status_proto"], +) + +py_proto_library( + name = "code_py_proto", + deps = [":code_proto"], +) + +py_proto_library( + name = "error_details_py_proto", + deps = [":error_details_proto"], +) + +py_proto_library( + name = "http_py_proto", + deps = [":http_proto"], +) + +py_proto_library( + name = "status_py_proto", + deps = [":status_proto"], +) + +# Open Source Packages +py_gapic_assembly_pkg( + name = "rpc-py", + deps = [ + ":code_proto", + ":code_py_proto", + ":error_details_proto", + ":error_details_py_proto", + ":http_proto", + ":http_py_proto", + ":status_proto", + ":status_py_proto", + ], +) + +php_proto_library( + name = "rpc_php_proto", + deps = [ + ":code_proto", + ":error_details_proto", + ":status_proto", + ], +) + +# Please DO-NOT-REMOVE this section. +# This is required to generate php files for these protos. +# Open Source Packages +php_gapic_assembly_pkg( + name = "google-rpc-php", + deps = [":rpc_php_proto"], +) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/README.md b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/README.md new file mode 100644 index 00000000..b4775007 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/README.md @@ -0,0 +1,18 @@ +## RPC (Remote Procedure Call) Types + +This package contains [protocol buffer][protobuf] types that represent remote procedure +call concepts. While [gRPC](https://grpc.io) uses these types, we encourage their +use in any interested RPC implementation to promote compatibility and consistency. + +### Key Concepts + +- **Code**: An enum that represents an error code returned by an RPC. These error codes + map to HTTP codes, but are slightly finer-grained. Every gRPC code has exactly one + corresponding HTTP code; however, some HTTP codes have more than one corresponding + gRPC code. +- **Error details**: Any of the types contained in `error_details.proto` which provide + extra details about particular types of failures. +- **Status**: Combines a code, message, and error details to represent the success or + failure details of an RPC call. + +[protobuf]: https://developers.google.com/protocol-buffers/ diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/code.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/code.proto new file mode 100644 index 00000000..aa6ce153 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/code.proto @@ -0,0 +1,186 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; +option java_multiple_files = true; +option java_outer_classname = "CodeProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The canonical error codes for gRPC APIs. +// +// +// Sometimes multiple error codes may apply. Services should return +// the most specific error code that applies. For example, prefer +// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. +// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. +enum Code { + // Not an error; returned on success. + // + // HTTP Mapping: 200 OK + OK = 0; + + // The operation was cancelled, typically by the caller. + // + // HTTP Mapping: 499 Client Closed Request + CANCELLED = 1; + + // Unknown error. For example, this error may be returned when + // a `Status` value received from another address space belongs to + // an error space that is not known in this address space. Also + // errors raised by APIs that do not return enough error information + // may be converted to this error. + // + // HTTP Mapping: 500 Internal Server Error + UNKNOWN = 2; + + // The client specified an invalid argument. Note that this differs + // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments + // that are problematic regardless of the state of the system + // (e.g., a malformed file name). + // + // HTTP Mapping: 400 Bad Request + INVALID_ARGUMENT = 3; + + // The deadline expired before the operation could complete. For operations + // that change the state of the system, this error may be returned + // even if the operation has completed successfully. For example, a + // successful response from a server could have been delayed long + // enough for the deadline to expire. + // + // HTTP Mapping: 504 Gateway Timeout + DEADLINE_EXCEEDED = 4; + + // Some requested entity (e.g., file or directory) was not found. + // + // Note to server developers: if a request is denied for an entire class + // of users, such as gradual feature rollout or undocumented allowlist, + // `NOT_FOUND` may be used. If a request is denied for some users within + // a class of users, such as user-based access control, `PERMISSION_DENIED` + // must be used. + // + // HTTP Mapping: 404 Not Found + NOT_FOUND = 5; + + // The entity that a client attempted to create (e.g., file or directory) + // already exists. + // + // HTTP Mapping: 409 Conflict + ALREADY_EXISTS = 6; + + // The caller does not have permission to execute the specified + // operation. `PERMISSION_DENIED` must not be used for rejections + // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` + // instead for those errors). `PERMISSION_DENIED` must not be + // used if the caller can not be identified (use `UNAUTHENTICATED` + // instead for those errors). This error code does not imply the + // request is valid or the requested entity exists or satisfies + // other pre-conditions. + // + // HTTP Mapping: 403 Forbidden + PERMISSION_DENIED = 7; + + // The request does not have valid authentication credentials for the + // operation. + // + // HTTP Mapping: 401 Unauthorized + UNAUTHENTICATED = 16; + + // Some resource has been exhausted, perhaps a per-user quota, or + // perhaps the entire file system is out of space. + // + // HTTP Mapping: 429 Too Many Requests + RESOURCE_EXHAUSTED = 8; + + // The operation was rejected because the system is not in a state + // required for the operation's execution. For example, the directory + // to be deleted is non-empty, an rmdir operation is applied to + // a non-directory, etc. + // + // Service implementors can use the following guidelines to decide + // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: + // (a) Use `UNAVAILABLE` if the client can retry just the failing call. + // (b) Use `ABORTED` if the client should retry at a higher level. For + // example, when a client-specified test-and-set fails, indicating the + // client should restart a read-modify-write sequence. + // (c) Use `FAILED_PRECONDITION` if the client should not retry until + // the system state has been explicitly fixed. For example, if an "rmdir" + // fails because the directory is non-empty, `FAILED_PRECONDITION` + // should be returned since the client should not retry unless + // the files are deleted from the directory. + // + // HTTP Mapping: 400 Bad Request + FAILED_PRECONDITION = 9; + + // The operation was aborted, typically due to a concurrency issue such as + // a sequencer check failure or transaction abort. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 409 Conflict + ABORTED = 10; + + // The operation was attempted past the valid range. E.g., seeking or + // reading past end-of-file. + // + // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `INVALID_ARGUMENT` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `OUT_OF_RANGE` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `FAILED_PRECONDITION` and + // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `OUT_OF_RANGE` error to detect when + // they are done. + // + // HTTP Mapping: 400 Bad Request + OUT_OF_RANGE = 11; + + // The operation is not implemented or is not supported/enabled in this + // service. + // + // HTTP Mapping: 501 Not Implemented + UNIMPLEMENTED = 12; + + // Internal errors. This means that some invariants expected by the + // underlying system have been broken. This error code is reserved + // for serious errors. + // + // HTTP Mapping: 500 Internal Server Error + INTERNAL = 13; + + // The service is currently unavailable. This is most likely a + // transient condition, which can be corrected by retrying with + // a backoff. Note that it is not always safe to retry + // non-idempotent operations. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 503 Service Unavailable + UNAVAILABLE = 14; + + // Unrecoverable data loss or corruption. + // + // HTTP Mapping: 500 Internal Server Error + DATA_LOSS = 15; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/error_details.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/error_details.proto new file mode 100644 index 00000000..4f9ecff0 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/error_details.proto @@ -0,0 +1,363 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; +option java_multiple_files = true; +option java_outer_classname = "ErrorDetailsProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Describes the cause of the error with structured details. +// +// Example of an error when contacting the "pubsub.googleapis.com" API when it +// is not enabled: +// +// { "reason": "API_DISABLED" +// "domain": "googleapis.com" +// "metadata": { +// "resource": "projects/123", +// "service": "pubsub.googleapis.com" +// } +// } +// +// This response indicates that the pubsub.googleapis.com API is not enabled. +// +// Example of an error that is returned when attempting to create a Spanner +// instance in a region that is out of stock: +// +// { "reason": "STOCKOUT" +// "domain": "spanner.googleapis.com", +// "metadata": { +// "availableRegions": "us-central1,us-east2" +// } +// } +message ErrorInfo { + // The reason of the error. This is a constant value that identifies the + // proximate cause of the error. Error reasons are unique within a particular + // domain of errors. This should be at most 63 characters and match a + // regular expression of `[A-Z][A-Z0-9_]+[A-Z0-9]`, which represents + // UPPER_SNAKE_CASE. + string reason = 1; + + // The logical grouping to which the "reason" belongs. The error domain + // is typically the registered service name of the tool or product that + // generates the error. Example: "pubsub.googleapis.com". If the error is + // generated by some common infrastructure, the error domain must be a + // globally unique value that identifies the infrastructure. For Google API + // infrastructure, the error domain is "googleapis.com". + string domain = 2; + + // Additional structured details about this error. + // + // Keys must match a regular expression of `[a-z][a-zA-Z0-9-_]+` but should + // ideally be lowerCamelCase. Also, they must be limited to 64 characters in + // length. When identifying the current value of an exceeded limit, the units + // should be contained in the key, not the value. For example, rather than + // `{"instanceLimit": "100/request"}`, should be returned as, + // `{"instanceLimitPerRequest": "100"}`, if the client exceeds the number of + // instances that can be created in a single (batch) request. + map metadata = 3; +} + +// Describes when the clients can retry a failed request. Clients could ignore +// the recommendation here or retry when this information is missing from error +// responses. +// +// It's always recommended that clients should use exponential backoff when +// retrying. +// +// Clients should wait until `retry_delay` amount of time has passed since +// receiving the error response before retrying. If retrying requests also +// fail, clients should use an exponential backoff scheme to gradually increase +// the delay between retries based on `retry_delay`, until either a maximum +// number of retries have been reached or a maximum retry delay cap has been +// reached. +message RetryInfo { + // Clients should wait at least this long between retrying the same request. + google.protobuf.Duration retry_delay = 1; +} + +// Describes additional debugging info. +message DebugInfo { + // The stack trace entries indicating where the error occurred. + repeated string stack_entries = 1; + + // Additional debugging information provided by the server. + string detail = 2; +} + +// Describes how a quota check failed. +// +// For example if a daily limit was exceeded for the calling project, +// a service could respond with a QuotaFailure detail containing the project +// id and the description of the quota limit that was exceeded. If the +// calling project hasn't enabled the service in the developer console, then +// a service could respond with the project id and set `service_disabled` +// to true. +// +// Also see RetryInfo and Help types for other details about handling a +// quota failure. +message QuotaFailure { + // A message type used to describe a single quota violation. For example, a + // daily quota or a custom quota that was exceeded. + message Violation { + // The subject on which the quota check failed. + // For example, "clientip:" or "project:". + string subject = 1; + + // A description of how the quota check failed. Clients can use this + // description to find more about the quota configuration in the service's + // public documentation, or find the relevant quota limit to adjust through + // developer console. + // + // For example: "Service disabled" or "Daily Limit for read operations + // exceeded". + string description = 2; + + // The API Service from which the `QuotaFailure.Violation` orginates. In + // some cases, Quota issues originate from an API Service other than the one + // that was called. In other words, a dependency of the called API Service + // could be the cause of the `QuotaFailure`, and this field would have the + // dependency API service name. + // + // For example, if the called API is Kubernetes Engine API + // (container.googleapis.com), and a quota violation occurs in the + // Kubernetes Engine API itself, this field would be + // "container.googleapis.com". On the other hand, if the quota violation + // occurs when the Kubernetes Engine API creates VMs in the Compute Engine + // API (compute.googleapis.com), this field would be + // "compute.googleapis.com". + string api_service = 3; + + // The metric of the violated quota. A quota metric is a named counter to + // measure usage, such as API requests or CPUs. When an activity occurs in a + // service, such as Virtual Machine allocation, one or more quota metrics + // may be affected. + // + // For example, "compute.googleapis.com/cpus_per_vm_family", + // "storage.googleapis.com/internet_egress_bandwidth". + string quota_metric = 4; + + // The id of the violated quota. Also know as "limit name", this is the + // unique identifier of a quota in the context of an API service. + // + // For example, "CPUS-PER-VM-FAMILY-per-project-region". + string quota_id = 5; + + // The dimensions of the violated quota. Every non-global quota is enforced + // on a set of dimensions. While quota metric defines what to count, the + // dimensions specify for what aspects the counter should be increased. + // + // For example, the quota "CPUs per region per VM family" enforces a limit + // on the metric "compute.googleapis.com/cpus_per_vm_family" on dimensions + // "region" and "vm_family". And if the violation occurred in region + // "us-central1" and for VM family "n1", the quota_dimensions would be, + // + // { + // "region": "us-central1", + // "vm_family": "n1", + // } + // + // When a quota is enforced globally, the quota_dimensions would always be + // empty. + map quota_dimensions = 6; + + // The enforced quota value at the time of the `QuotaFailure`. + // + // For example, if the enforced quota value at the time of the + // `QuotaFailure` on the number of CPUs is "10", then the value of this + // field would reflect this quantity. + int64 quota_value = 7; + + // The new quota value being rolled out at the time of the violation. At the + // completion of the rollout, this value will be enforced in place of + // quota_value. If no rollout is in progress at the time of the violation, + // this field is not set. + // + // For example, if at the time of the violation a rollout is in progress + // changing the number of CPUs quota from 10 to 20, 20 would be the value of + // this field. + optional int64 future_quota_value = 8; + } + + // Describes all quota violations. + repeated Violation violations = 1; +} + +// Describes what preconditions have failed. +// +// For example, if an RPC failed because it required the Terms of Service to be +// acknowledged, it could list the terms of service violation in the +// PreconditionFailure message. +message PreconditionFailure { + // A message type used to describe a single precondition failure. + message Violation { + // The type of PreconditionFailure. We recommend using a service-specific + // enum type to define the supported precondition violation subjects. For + // example, "TOS" for "Terms of Service violation". + string type = 1; + + // The subject, relative to the type, that failed. + // For example, "google.com/cloud" relative to the "TOS" type would indicate + // which terms of service is being referenced. + string subject = 2; + + // A description of how the precondition failed. Developers can use this + // description to understand how to fix the failure. + // + // For example: "Terms of service not accepted". + string description = 3; + } + + // Describes all precondition violations. + repeated Violation violations = 1; +} + +// Describes violations in a client request. This error type focuses on the +// syntactic aspects of the request. +message BadRequest { + // A message type used to describe a single bad request field. + message FieldViolation { + // A path that leads to a field in the request body. The value will be a + // sequence of dot-separated identifiers that identify a protocol buffer + // field. + // + // Consider the following: + // + // message CreateContactRequest { + // message EmailAddress { + // enum Type { + // TYPE_UNSPECIFIED = 0; + // HOME = 1; + // WORK = 2; + // } + // + // optional string email = 1; + // repeated EmailType type = 2; + // } + // + // string full_name = 1; + // repeated EmailAddress email_addresses = 2; + // } + // + // In this example, in proto `field` could take one of the following values: + // + // * `full_name` for a violation in the `full_name` value + // * `email_addresses[1].email` for a violation in the `email` field of the + // first `email_addresses` message + // * `email_addresses[3].type[2]` for a violation in the second `type` + // value in the third `email_addresses` message. + // + // In JSON, the same values are represented as: + // + // * `fullName` for a violation in the `fullName` value + // * `emailAddresses[1].email` for a violation in the `email` field of the + // first `emailAddresses` message + // * `emailAddresses[3].type[2]` for a violation in the second `type` + // value in the third `emailAddresses` message. + string field = 1; + + // A description of why the request element is bad. + string description = 2; + + // The reason of the field-level error. This is a constant value that + // identifies the proximate cause of the field-level error. It should + // uniquely identify the type of the FieldViolation within the scope of the + // google.rpc.ErrorInfo.domain. This should be at most 63 + // characters and match a regular expression of `[A-Z][A-Z0-9_]+[A-Z0-9]`, + // which represents UPPER_SNAKE_CASE. + string reason = 3; + + // Provides a localized error message for field-level errors that is safe to + // return to the API consumer. + LocalizedMessage localized_message = 4; + } + + // Describes all violations in a client request. + repeated FieldViolation field_violations = 1; +} + +// Contains metadata about the request that clients can attach when filing a bug +// or providing other forms of feedback. +message RequestInfo { + // An opaque string that should only be interpreted by the service generating + // it. For example, it can be used to identify requests in the service's logs. + string request_id = 1; + + // Any data that was used to serve this request. For example, an encrypted + // stack trace that can be sent back to the service provider for debugging. + string serving_data = 2; +} + +// Describes the resource that is being accessed. +message ResourceInfo { + // A name for the type of resource being accessed, e.g. "sql table", + // "cloud storage bucket", "file", "Google calendar"; or the type URL + // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". + string resource_type = 1; + + // The name of the resource being accessed. For example, a shared calendar + // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current + // error is + // [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + string resource_name = 2; + + // The owner of the resource (optional). + // For example, "user:" or "project:". + string owner = 3; + + // Describes what error is encountered when accessing this resource. + // For example, updating a cloud project may require the `writer` permission + // on the developer console project. + string description = 4; +} + +// Provides links to documentation or for performing an out of band action. +// +// For example, if a quota check failed with an error indicating the calling +// project hasn't enabled the accessed service, this can contain a URL pointing +// directly to the right place in the developer console to flip the bit. +message Help { + // Describes a URL link. + message Link { + // Describes what the link offers. + string description = 1; + + // The URL of the link. + string url = 2; + } + + // URL(s) pointing to additional information on handling the current error. + repeated Link links = 1; +} + +// Provides a localized error message that is safe to return to the user +// which can be attached to an RPC error. +message LocalizedMessage { + // The locale used following the specification defined at + // https://www.rfc-editor.org/rfc/bcp/bcp47.txt. + // Examples are: "en-US", "fr-CH", "es-MX" + string locale = 1; + + // The localized error message in the above locale. + string message = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/http.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/http.proto new file mode 100644 index 00000000..ba59c2b7 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/http.proto @@ -0,0 +1,64 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/http;http"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Represents an HTTP request. +message HttpRequest { + // The HTTP request method. + string method = 1; + + // The HTTP request URI. + string uri = 2; + + // The HTTP request headers. The ordering of the headers is significant. + // Multiple headers with the same key may present for the request. + repeated HttpHeader headers = 3; + + // The HTTP request body. If the body is not expected, it should be empty. + bytes body = 4; +} + +// Represents an HTTP response. +message HttpResponse { + // The HTTP status code, such as 200 or 404. + int32 status = 1; + + // The HTTP reason phrase, such as "OK" or "Not Found". + string reason = 2; + + // The HTTP response headers. The ordering of the headers is significant. + // Multiple headers with the same key may present for the response. + repeated HttpHeader headers = 3; + + // The HTTP response body. If the body is not expected, it should be empty. + bytes body = 4; +} + +// Represents an HTTP header. +message HttpHeader { + // The HTTP header key. It is case insensitive. + string key = 1; + + // The HTTP header value. + string value = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/rpc_publish.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/rpc_publish.yaml new file mode 100644 index 00000000..90a91e07 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/rpc_publish.yaml @@ -0,0 +1,28 @@ +type: google.api.Service +config_version: 1 +name: rpc.googleapis.com +title: Google RPC Types + +types: +- name: google.rpc.Status +- name: google.rpc.RetryInfo +- name: google.rpc.DebugInfo +- name: google.rpc.QuotaFailure +- name: google.rpc.BadRequest +- name: google.rpc.PreconditionFailure +- name: google.rpc.RequestInfo +- name: google.rpc.ResourceInfo +- name: google.rpc.Help +- name: google.rpc.LocalizedMessage + +enums: +- name: google.rpc.Code + +documentation: + summary: Defines RPC types. + overview: |- + # Google RPC + + This package contains type definitions for general RPC systems. While + [gRPC](https://github.com/grpc) is using these defintions, they are not + designed specifically to support gRPC. diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/status.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/status.proto new file mode 100644 index 00000000..dc14c943 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/rpc/status.proto @@ -0,0 +1,49 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; +option java_multiple_files = true; +option java_outer_classname = "StatusProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// The `Status` type defines a logical error model that is suitable for +// different programming environments, including REST APIs and RPC APIs. It is +// used by [gRPC](https://github.com/grpc). Each `Status` message contains +// three pieces of data: error code, error message, and error details. +// +// You can find out more about this error model and how to work with it in the +// [API Design Guide](https://cloud.google.com/apis/design/errors). +message Status { + // The status code, which should be an enum value of + // [google.rpc.Code][google.rpc.Code]. + int32 code = 1; + + // A developer-facing error message, which should be in English. Any + // user-facing error message should be localized and sent in the + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized + // by the client. + string message = 2; + + // A list of messages that carry the error details. There is a common set of + // message types for APIs to use. + repeated google.protobuf.Any details = 3; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/BUILD.bazel new file mode 100644 index 00000000..a87c57fe --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/BUILD.bazel @@ -0,0 +1 @@ +exports_files(glob(["*.yaml"])) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/BUILD.bazel new file mode 100644 index 00000000..541f34cf --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/BUILD.bazel @@ -0,0 +1,380 @@ +# This file was automatically generated by BuildFileGenerator +# https://github.com/googleapis/rules_gapic/tree/master/bazel + +# Most of the manual changes to this file will be overwritten. +# It's **only** allowed to change the following rule attribute values: +# - names of *_gapic_assembly_* rules +# - certain parameters of *_gapic_library rules, including but not limited to: +# * extra_protoc_parameters +# * extra_protoc_file_parameters +# The complete list of preserved parameters can be found in the source code. + +# buildifier: disable=load-on-top + +# This is an API workspace, having public visibility by default makes perfect sense. +package(default_visibility = ["//visibility:public"]) + +############################################################################## +# Common +############################################################################## +# buildifier: disable=same-origin-load +load("@com_google_googleapis_imports//:imports.bzl", "proto_library_with_info") +load("@rules_proto//proto:defs.bzl", "proto_library") + +proto_library( + name = "adapter_proto", + srcs = [ + "adapter.proto", + ], + deps = [ + "//google/api:annotations_proto", + "//google/api:client_proto", + "//google/api:field_behavior_proto", + "//google/api:resource_proto", + ], +) + +proto_library_with_info( + name = "adapter_proto_with_info", + deps = [ + ":adapter_proto", + "//google/cloud:common_resources_proto", + ], +) + +############################################################################## +# Java +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "java_gapic_assembly_gradle_pkg", + "java_gapic_library", + "java_gapic_test", + "java_grpc_library", + "java_proto_library", +) + +java_proto_library( + name = "adapter_java_proto", + deps = [":adapter_proto"], +) + +java_grpc_library( + name = "adapter_java_grpc", + srcs = [":adapter_proto"], + deps = [":adapter_java_proto"], +) + +java_gapic_library( + name = "adapter_java_gapic", + srcs = [":adapter_proto_with_info"], + gapic_yaml = None, + grpc_service_config = "spanner_adapter_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + test_deps = [ + ":adapter_java_grpc", + ], + transport = "grpc+rest", + deps = [ + ":adapter_java_proto", + "//google/api:api_java_proto", + ], +) + +java_gapic_test( + name = "adapter_java_gapic_test_suite", + test_classes = [ + "com.google.spanner.adapter.v1.AdapterClientHttpJsonTest", + "com.google.spanner.adapter.v1.AdapterClientTest", + ], + runtime_deps = [":adapter_java_gapic_test"], +) + +# Open Source Packages +java_gapic_assembly_gradle_pkg( + name = "google-cloud-spanner-adapter-v1-java", + include_samples = True, + transport = "grpc+rest", + deps = [ + ":adapter_java_gapic", + ":adapter_java_grpc", + ":adapter_java_proto", + ":adapter_proto", + ], +) + +############################################################################## +# Go +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "go_gapic_assembly_pkg", + "go_gapic_library", + "go_proto_library", +) + +go_proto_library( + name = "adapter_go_proto", + compilers = ["@io_bazel_rules_go//proto:go_grpc"], + importpath = "cloud.google.com/go/spanner/adapter/apiv1/adapterpb", + protos = [":adapter_proto"], + deps = [ + "//google/api:annotations_go_proto", + ], +) + +go_gapic_library( + name = "adapter_go_gapic", + srcs = [":adapter_proto_with_info"], + grpc_service_config = "spanner_adapter_grpc_service_config.json", + importpath = "cloud.google.com/go/spanner/adapter/apiv1;adapter", + metadata = True, + release_level = "beta", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":adapter_go_proto", + ], +) + +# Open Source Packages +go_gapic_assembly_pkg( + name = "gapi-cloud-spanner-adapter-v1-go", + deps = [ + ":adapter_go_gapic", + ":adapter_go_gapic_srcjar-metadata.srcjar", + ":adapter_go_gapic_srcjar-snippets.srcjar", + ":adapter_go_gapic_srcjar-test.srcjar", + ":adapter_go_proto", + ], +) + +############################################################################## +# Python +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "py_gapic_assembly_pkg", + "py_gapic_library", + "py_test", +) + +py_gapic_library( + name = "adapter_py_gapic", + srcs = [":adapter_proto"], + grpc_service_config = "spanner_adapter_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ], +) + +py_test( + name = "adapter_py_gapic_test", + srcs = [ + "adapter_py_gapic_pytest.py", + "adapter_py_gapic_test.py", + ], + legacy_create_init = False, + deps = [":adapter_py_gapic"], +) + +# Open Source Packages +py_gapic_assembly_pkg( + name = "spanner-adapter-v1-py", + deps = [ + ":adapter_py_gapic", + ], +) + +############################################################################## +# PHP +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "php_gapic_assembly_pkg", + "php_gapic_library", + "php_proto_library", +) + +php_proto_library( + name = "adapter_php_proto", + deps = [":adapter_proto"], +) + +php_gapic_library( + name = "adapter_php_gapic", + srcs = [":adapter_proto_with_info"], + grpc_service_config = "spanner_adapter_grpc_service_config.json", + migration_mode = "NEW_SURFACE_ONLY", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":adapter_php_proto", + ], +) + +# Open Source Packages +php_gapic_assembly_pkg( + name = "google-cloud-spanner-adapter-v1-php", + deps = [ + ":adapter_php_gapic", + ":adapter_php_proto", + ], +) + +############################################################################## +# Node.js +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "nodejs_gapic_assembly_pkg", + "nodejs_gapic_library", +) + +nodejs_gapic_library( + name = "adapter_nodejs_gapic", + package_name = "@google-cloud/adapter", + src = ":adapter_proto_with_info", + extra_protoc_parameters = ["metadata"], + grpc_service_config = "spanner_adapter_grpc_service_config.json", + package = "google.spanner.adapter.v1", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [], +) + +nodejs_gapic_assembly_pkg( + name = "spanner-adapter-v1-nodejs", + deps = [ + ":adapter_nodejs_gapic", + ":adapter_proto", + ], +) + +############################################################################## +# Ruby +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "ruby_cloud_gapic_library", + "ruby_gapic_assembly_pkg", + "ruby_grpc_library", + "ruby_proto_library", +) + +ruby_proto_library( + name = "adapter_ruby_proto", + deps = [":adapter_proto"], +) + +ruby_grpc_library( + name = "adapter_ruby_grpc", + srcs = [":adapter_proto"], + deps = [":adapter_ruby_proto"], +) + +ruby_cloud_gapic_library( + name = "adapter_ruby_gapic", + srcs = [":adapter_proto_with_info"], + extra_protoc_parameters = ["ruby-cloud-gem-name=google-cloud-spanner-adapter-v1"], + grpc_service_config = "spanner_adapter_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":adapter_ruby_grpc", + ":adapter_ruby_proto", + ], +) + +# Open Source Packages +ruby_gapic_assembly_pkg( + name = "google-cloud-spanner-adapter-v1-ruby", + deps = [ + ":adapter_ruby_gapic", + ":adapter_ruby_grpc", + ":adapter_ruby_proto", + ], +) + +############################################################################## +# C# +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "csharp_gapic_assembly_pkg", + "csharp_gapic_library", + "csharp_grpc_library", + "csharp_proto_library", +) + +csharp_proto_library( + name = "adapter_csharp_proto", + extra_opts = [], + deps = [":adapter_proto"], +) + +csharp_grpc_library( + name = "adapter_csharp_grpc", + srcs = [":adapter_proto"], + deps = [":adapter_csharp_proto"], +) + +csharp_gapic_library( + name = "adapter_csharp_gapic", + srcs = [":adapter_proto_with_info"], + common_resources_config = "@gax_dotnet//:Google.Api.Gax/ResourceNames/CommonResourcesConfig.json", + grpc_service_config = "spanner_adapter_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":adapter_csharp_grpc", + ":adapter_csharp_proto", + ], +) + +# Open Source Packages +csharp_gapic_assembly_pkg( + name = "google-cloud-spanner-adapter-v1-csharp", + deps = [ + ":adapter_csharp_gapic", + ":adapter_csharp_grpc", + ":adapter_csharp_proto", + ], +) + +############################################################################## +# C++ +############################################################################## +# buildifier: disable=same-origin-load +load( + "@com_google_googleapis_imports//:imports.bzl", + "cc_grpc_library", + "cc_proto_library", +) + +cc_proto_library( + name = "adapter_cc_proto", + deps = [":adapter_proto"], +) + +cc_grpc_library( + name = "adapter_cc_grpc", + srcs = [":adapter_proto"], + grpc_only = True, + deps = [":adapter_cc_proto"], +) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/adapter.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/adapter.proto new file mode 100644 index 00000000..bc3afa35 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/adapter.proto @@ -0,0 +1,123 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.adapter.v1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Adapter.V1"; +option go_package = "cloud.google.com/go/spanner/adapter/apiv1/adapterpb;adapterpb"; +option java_multiple_files = true; +option java_outer_classname = "AdapterProto"; +option java_package = "com.google.spanner.adapter.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Adapter\\V1"; +option ruby_package = "Google::Cloud::Spanner::Adapter::V1"; +option (google.api.resource_definition) = { + type: "spanner.googleapis.com/Database" + pattern: "projects/{project}/instances/{instance}/databases/{database}" +}; + +// Cloud Spanner Adapter API +// +// The Cloud Spanner Adapter service allows native drivers of supported database +// dialects to interact directly with Cloud Spanner by wrapping the underlying +// wire protocol used by the driver in a gRPC stream. +service Adapter { + option (google.api.default_host) = "spanner.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/spanner.data"; + + // Creates a new session to be used for requests made by the adapter. + // A session identifies a specific incarnation of a database resource and is + // meant to be reused across many `AdaptMessage` calls. + rpc CreateSession(CreateSessionRequest) returns (Session) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*/databases/*}/sessions:adapter" + body: "session" + }; + option (google.api.method_signature) = "parent,session"; + } + + // Handles a single message from the client and returns the result as a + // stream. The server will interpret the message frame and respond with + // message frames to the client. + rpc AdaptMessage(AdaptMessageRequest) returns (stream AdaptMessageResponse) { + option (google.api.http) = { + post: "/v1/{name=projects/*/instances/*/databases/*/sessions/*}:adaptMessage" + body: "*" + }; + } +} + +// Message sent by the client to the adapter. +message AdaptMessageRequest { + // Required. The database session in which the adapter request is processed. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Required. Identifier for the underlying wire protocol. + string protocol = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Uninterpreted bytes from the underlying wire protocol. + bytes payload = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Opaque request state passed by the client to the server. + map attachments = 4 [(google.api.field_behavior) = OPTIONAL]; +} + +// Message sent by the adapter to the client. +message AdaptMessageResponse { + // Optional. Uninterpreted bytes from the underlying wire protocol. + bytes payload = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Opaque state updates to be applied by the client. + map state_updates = 2 + [(google.api.field_behavior) = OPTIONAL]; +} + +// A session in the Cloud Spanner Adapter API. +message Session { + option (google.api.resource) = { + type: "spanner.googleapis.com/Session" + pattern: "projects/{project}/instances/{instance}/databases/{database}/sessions/{session}" + plural: "sessions" + singular: "session" + }; + + // Identifier. The name of the session. This is always system-assigned. + string name = 1 [(google.api.field_behavior) = IDENTIFIER]; +} + +// The request for +// [CreateSessionRequest][Adapter.CreateSessionRequest]. +message CreateSessionRequest { + // Required. The database in which the new session is created. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Required. The session to create. + Session session = 2 [(google.api.field_behavior) = REQUIRED]; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/spanner.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/spanner.yaml new file mode 100644 index 00000000..dad94006 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/spanner.yaml @@ -0,0 +1,69 @@ +type: google.api.Service +config_version: 3 +name: spanner.googleapis.com +title: Cloud Spanner API + +apis: +- name: google.spanner.adapter.v1.Adapter + +documentation: + summary: |- + Cloud Spanner is a managed, mission-critical, globally consistent and + scalable relational database service. + +http: + rules: + - selector: google.longrunning.Operations.CancelOperation + post: '/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel' + additional_bindings: + - post: '/v1/{name=projects/*/instances/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instances/*/backups/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instanceConfigs/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}:cancel' + - selector: google.longrunning.Operations.DeleteOperation + delete: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - delete: '/v1/{name=projects/*/instances/*/operations/*}' + - delete: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - delete: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}' + - delete: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - delete: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}' + - selector: google.longrunning.Operations.GetOperation + get: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations/*}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - get: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - get: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}' + - selector: google.longrunning.Operations.ListOperations + get: '/v1/{name=projects/*/instances/*/databases/*/operations}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations}' + - get: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations}' + - get: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations}' + +authentication: + rules: + - selector: 'google.longrunning.Operations.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.admin + - selector: google.spanner.adapter.v1.Adapter.AdaptMessage + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.data + - selector: google.spanner.adapter.v1.Adapter.CreateSession + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.data + +publishing: + new_issue_uri: https://issuetracker.google.com/issues/new?component=190851&template=0 + documentation_uri: https://cloud.google.com/spanner/ diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/spanner_adapter_grpc_service_config.json b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/spanner_adapter_grpc_service_config.json new file mode 100644 index 00000000..11e16d5f --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/adapter/v1/spanner_adapter_grpc_service_config.json @@ -0,0 +1,31 @@ +{ + "methodConfig": [ + { + "name": [ + { + "service": "google.spanner.adapter.v1.Adapter", + "method": "AdaptMessage" + } + ], + "timeout": "3600s" + }, + { + "name": [ + { + "service": "google.spanner.adapter.v1.Adapter", + "method": "CreateSession" + } + ], + "timeout": "30s", + "retryPolicy": { + "initialBackoff": "0.250s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "RESOURCE_EXHAUSTED" + ] + } + } + ] +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/BUILD.bazel new file mode 100644 index 00000000..a87c57fe --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/BUILD.bazel @@ -0,0 +1 @@ +exports_files(glob(["*.yaml"])) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/BUILD.bazel new file mode 100644 index 00000000..86189f7e --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/BUILD.bazel @@ -0,0 +1,351 @@ +# This file was automatically generated by BuildFileGenerator +# https://github.com/googleapis/rules_gapic/tree/master/bazel + +# Most of the manual changes to this file will be overwritten. +# It's **only** allowed to change the following rule attribute values: +# - names of *_gapic_assembly_* rules +# - certain parameters of *_gapic_library rules, including but not limited to: +# * extra_protoc_parameters +# * extra_protoc_file_parameters +# The complete list of preserved parameters can be found in the source code. + +load( + "@com_google_googleapis_imports//:imports.bzl", + "cc_grpc_library", + "cc_proto_library", + "csharp_gapic_assembly_pkg", + "csharp_gapic_library", + "csharp_grpc_library", + "csharp_proto_library", + "go_gapic_assembly_pkg", + "go_gapic_library", + "go_proto_library", + "java_gapic_assembly_gradle_pkg", + "java_gapic_library", + "java_gapic_test", + "java_grpc_library", + "java_proto_library", + "nodejs_gapic_assembly_pkg", + "nodejs_gapic_library", + "php_gapic_assembly_pkg", + "php_gapic_library", + "php_proto_library", + "proto_library_with_info", + "py_gapic_assembly_pkg", + "py_gapic_library", + "py_test", + "ruby_cloud_gapic_library", + "ruby_gapic_assembly_pkg", + "ruby_grpc_library", + "ruby_proto_library", +) + +############################################################################## +# Common +############################################################################## +load("@rules_proto//proto:defs.bzl", "proto_library") + +# This is an API workspace, having public visibility by default makes perfect sense. +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "database_proto", + srcs = [ + "backup.proto", + "backup_schedule.proto", + "common.proto", + "spanner_database_admin.proto", + ], + deps = [ + "//google/api:annotations_proto", + "//google/api:client_proto", + "//google/api:field_behavior_proto", + "//google/api:resource_proto", + "//google/iam/v1:iam_policy_proto", + "//google/iam/v1:policy_proto", + "//google/longrunning:operations_proto", + "//google/rpc:status_proto", + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:empty_proto", + "@com_google_protobuf//:field_mask_proto", + "@com_google_protobuf//:struct_proto", + "@com_google_protobuf//:timestamp_proto", + ], +) + +proto_library_with_info( + name = "database_proto_with_info", + deps = [ + ":database_proto", + "//google/cloud:common_resources_proto", + ], +) + +java_proto_library( + name = "database_java_proto", + deps = [":database_proto"], +) + +java_grpc_library( + name = "database_java_grpc", + srcs = [":database_proto"], + deps = [":database_java_proto"], +) + +java_gapic_library( + name = "database_java_gapic", + srcs = [":database_proto_with_info"], + gapic_yaml = "spanner_gapic.yaml", + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + test_deps = [ + ":database_java_grpc", + "//google/iam/v1:iam_java_grpc", + ], + transport = "grpc+rest", + deps = [ + ":database_java_proto", + "//google/api:api_java_proto", + "//google/iam/v1:iam_java_proto", + ], +) + +java_gapic_test( + name = "database_java_gapic_test_suite", + test_classes = [ + "com.google.cloud.spanner.admin.database.v1.DatabaseAdminClientHttpJsonTest", + "com.google.cloud.spanner.admin.database.v1.DatabaseAdminClientTest", + ], + runtime_deps = [":database_java_gapic_test"], +) + +# Open Source Packages +java_gapic_assembly_gradle_pkg( + name = "google-cloud-admin-database-v1-java", + include_samples = True, + transport = "grpc+rest", + deps = [ + ":database_java_gapic", + ":database_java_grpc", + ":database_java_proto", + ":database_proto", + ], +) + +go_proto_library( + name = "database_go_proto", + compilers = ["@io_bazel_rules_go//proto:go_grpc"], + importpath = "cloud.google.com/go/spanner/admin/database/apiv1/databasepb", + protos = [":database_proto"], + deps = [ + "//google/api:annotations_go_proto", + "//google/iam/v1:iam_go_proto", + "//google/longrunning:longrunning_go_proto", + "//google/rpc:status_go_proto", + ], +) + +go_gapic_library( + name = "database_go_gapic", + srcs = [":database_proto_with_info"], + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + importpath = "cloud.google.com/go/spanner/admin/database/apiv1;database", + release_level = "ga", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":database_go_proto", + "//google/iam/v1:iam_go_proto", + "//google/longrunning:longrunning_go_proto", + "@com_google_cloud_go_longrunning//:go_default_library", + "@com_google_cloud_go_longrunning//autogen:go_default_library", + ], +) + +# Open Source Packages +go_gapic_assembly_pkg( + name = "gapi-cloud-admin-database-v1-go", + deps = [ + ":database_go_gapic", + ":database_go_gapic_srcjar-snippets.srcjar", + ":database_go_gapic_srcjar-test.srcjar", + ":database_go_proto", + ], +) + +py_gapic_library( + name = "database_py_gapic", + srcs = [":database_proto"], + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + opt_args = [ + "python-gapic-namespace=google.cloud", + "python-gapic-name=spanner_admin_database", + ], + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + "//google/iam/v1:iam_policy_py_proto", + ], +) + +py_test( + name = "database_py_gapic_test", + srcs = [ + "database_py_gapic_pytest.py", + "database_py_gapic_test.py", + ], + legacy_create_init = False, + deps = [":database_py_gapic"], +) + +py_gapic_assembly_pkg( + name = "admin-database-v1-py", + deps = [ + ":database_py_gapic", + ], +) + +php_proto_library( + name = "database_php_proto", + deps = [":database_proto"], +) + +php_gapic_library( + name = "database_php_gapic", + srcs = [":database_proto_with_info"], + gapic_yaml = "spanner_gapic.yaml", + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + migration_mode = "MIGRATING", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [":database_php_proto"], +) + +# Open Source Packages +php_gapic_assembly_pkg( + name = "google-cloud-admin-database-v1-php", + deps = [ + ":database_php_gapic", + ":database_php_proto", + ], +) + +nodejs_gapic_library( + name = "database_nodejs_gapic", + package_name = "@google-cloud/spanner", + src = ":database_proto_with_info", + extra_protoc_parameters = [ + "metadata", + "template=typescript_gapic", + ], + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + handwritten_layer = True, + package = "google.spanner.admin.database.v1", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [], +) + +nodejs_gapic_assembly_pkg( + name = "admin-database-v1-nodejs", + deps = [ + ":database_nodejs_gapic", + ":database_proto", + ], +) + +ruby_proto_library( + name = "database_ruby_proto", + deps = [":database_proto"], +) + +ruby_grpc_library( + name = "database_ruby_grpc", + srcs = [":database_proto"], + deps = [":database_ruby_proto"], +) + +ruby_cloud_gapic_library( + name = "database_ruby_gapic", + srcs = [":database_proto_with_info"], + extra_protoc_parameters = [ + "ruby-cloud-gem-name=google-cloud-spanner-admin-database-v1", + "ruby-cloud-env-prefix=SPANNER", + "ruby-cloud-product-url=https://cloud.google.com/spanner", + "ruby-cloud-api-id=spanner.googleapis.com", + "ruby-cloud-api-shortname=spanner", + "ruby-cloud-wrapper-gem-override=google-cloud-spanner", + ], + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + rest_numeric_enums = True, + ruby_cloud_description = "Cloud Spanner is a managed, mission-critical, globally consistent and scalable relational database service.", + ruby_cloud_title = "Cloud Spanner Database Admin V1", + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":database_ruby_grpc", + ":database_ruby_proto", + ], +) + +# Open Source Packages +ruby_gapic_assembly_pkg( + name = "google-cloud-admin-database-v1-ruby", + deps = [ + ":database_ruby_gapic", + ":database_ruby_grpc", + ":database_ruby_proto", + ], +) + +csharp_proto_library( + name = "database_csharp_proto", + deps = [":database_proto"], +) + +csharp_grpc_library( + name = "database_csharp_grpc", + srcs = [":database_proto"], + deps = [":database_csharp_proto"], +) + +csharp_gapic_library( + name = "database_csharp_gapic", + srcs = [":database_proto_with_info"], + common_resources_config = "@gax_dotnet//:Google.Api.Gax/ResourceNames/CommonResourcesConfig.json", + grpc_service_config = "spanner_admin_database_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":database_csharp_grpc", + ":database_csharp_proto", + ], +) + +# Open Source Packages +csharp_gapic_assembly_pkg( + name = "google-cloud-admin-database-v1-csharp", + deps = [ + ":database_csharp_gapic", + ":database_csharp_grpc", + ":database_csharp_proto", + ], +) + +cc_proto_library( + name = "database_cc_proto", + deps = [":database_proto"], +) + +cc_grpc_library( + name = "database_cc_grpc", + srcs = [":database_proto"], + grpc_only = True, + deps = [":database_cc_proto"], +) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/backup.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/backup.proto new file mode 100644 index 00000000..6898814c --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/backup.proto @@ -0,0 +1,773 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.admin.database.v1; + +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/spanner/admin/database/v1/common.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Admin.Database.V1"; +option go_package = "cloud.google.com/go/spanner/admin/database/apiv1/databasepb;databasepb"; +option java_multiple_files = true; +option java_outer_classname = "BackupProto"; +option java_package = "com.google.spanner.admin.database.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Database\\V1"; +option ruby_package = "Google::Cloud::Spanner::Admin::Database::V1"; + +// A backup of a Cloud Spanner database. +message Backup { + option (google.api.resource) = { + type: "spanner.googleapis.com/Backup" + pattern: "projects/{project}/instances/{instance}/backups/{backup}" + }; + + // Indicates the current state of the backup. + enum State { + // Not specified. + STATE_UNSPECIFIED = 0; + + // The pending backup is still being created. Operations on the + // backup may fail with `FAILED_PRECONDITION` in this state. + CREATING = 1; + + // The backup is complete and ready for use. + READY = 2; + } + + // Required for the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // operation. Name of the database from which this backup was created. This + // needs to be in the same instance as the backup. Values are of the form + // `projects//instances//databases/`. + string database = 2 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; + + // The backup will contain an externally consistent copy of the database at + // the timestamp specified by `version_time`. If `version_time` is not + // specified, the system will set `version_time` to the `create_time` of the + // backup. + google.protobuf.Timestamp version_time = 9; + + // Required for the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // operation. The expiration time of the backup, with microseconds + // granularity that must be at least 6 hours and at most 366 days + // from the time the CreateBackup request is processed. Once the `expire_time` + // has passed, the backup is eligible to be automatically deleted by Cloud + // Spanner to free the resources used by the backup. + google.protobuf.Timestamp expire_time = 3; + + // Output only for the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // operation. Required for the + // [UpdateBackup][google.spanner.admin.database.v1.DatabaseAdmin.UpdateBackup] + // operation. + // + // A globally unique identifier for the backup which cannot be + // changed. Values are of the form + // `projects//instances//backups/[a-z][a-z0-9_\-]*[a-z0-9]` + // The final segment of the name must be between 2 and 60 characters + // in length. + // + // The backup is stored in the location(s) specified in the instance + // configuration of the instance containing the backup, identified + // by the prefix of the backup name of the form + // `projects//instances/`. + string name = 1; + + // Output only. The time the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // request is received. If the request does not specify `version_time`, the + // `version_time` of the backup will be equivalent to the `create_time`. + google.protobuf.Timestamp create_time = 4 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Size of the backup in bytes. + int64 size_bytes = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The number of bytes that will be freed by deleting this + // backup. This value will be zero if, for example, this backup is part of an + // incremental backup chain and younger backups in the chain require that we + // keep its data. For backups not in an incremental backup chain, this is + // always the size of the backup. This value may change if backups on the same + // chain get created, deleted or expired. + int64 freeable_size_bytes = 15 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. For a backup in an incremental backup chain, this is the + // storage space needed to keep the data that has changed since the previous + // backup. For all other backups, this is always the size of the backup. This + // value may change if backups on the same chain get deleted or expired. + // + // This field can be used to calculate the total storage space used by a set + // of backups. For example, the total space used by all backups of a database + // can be computed by summing up this field. + int64 exclusive_size_bytes = 16 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The current state of the backup. + State state = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The names of the restored databases that reference the backup. + // The database names are of + // the form `projects//instances//databases/`. + // Referencing databases may exist in different instances. The existence of + // any referencing database prevents the backup from being deleted. When a + // restored database from the backup enters the `READY` state, the reference + // to the backup is removed. + repeated string referencing_databases = 7 [ + (google.api.field_behavior) = OUTPUT_ONLY, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Output only. The encryption information for the backup. + EncryptionInfo encryption_info = 8 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The encryption information for the backup, whether it is + // protected by one or more KMS keys. The information includes all Cloud + // KMS key versions used to encrypt the backup. The `encryption_status' field + // inside of each `EncryptionInfo` is not populated. At least one of the key + // versions must be available for the backup to be restored. If a key version + // is revoked in the middle of a restore, the restore behavior is undefined. + repeated EncryptionInfo encryption_information = 13 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The database dialect information for the backup. + DatabaseDialect database_dialect = 10 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The names of the destination backups being created by copying + // this source backup. The backup names are of the form + // `projects//instances//backups/`. + // Referencing backups may exist in different instances. The existence of + // any referencing backup prevents the backup from being deleted. When the + // copy operation is done (either successfully completed or cancelled or the + // destination backup is deleted), the reference to the backup is removed. + repeated string referencing_backups = 11 [ + (google.api.field_behavior) = OUTPUT_ONLY, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; + + // Output only. The max allowed expiration time of the backup, with + // microseconds granularity. A backup's expiration time can be configured in + // multiple APIs: CreateBackup, UpdateBackup, CopyBackup. When updating or + // copying an existing backup, the expiration time specified must be + // less than `Backup.max_expire_time`. + google.protobuf.Timestamp max_expire_time = 12 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. List of backup schedule URIs that are associated with + // creating this backup. This is only applicable for scheduled backups, and + // is empty for on-demand backups. + // + // To optimize for storage, whenever possible, multiple schedules are + // collapsed together to create one backup. In such cases, this field captures + // the list of all backup schedule URIs that are associated with creating + // this backup. If collapsing is not done, then this field captures the + // single backup schedule URI associated with creating this backup. + repeated string backup_schedules = 14 [ + (google.api.field_behavior) = OUTPUT_ONLY, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/BackupSchedule" + } + ]; + + // Output only. Populated only for backups in an incremental backup chain. + // Backups share the same chain id if and only if they belong to the same + // incremental backup chain. Use this field to determine which backups are + // part of the same incremental backup chain. The ordering of backups in the + // chain can be determined by ordering the backup `version_time`. + string incremental_backup_chain_id = 17 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Data deleted at a time older than this is guaranteed not to be + // retained in order to support this backup. For a backup in an incremental + // backup chain, this is the version time of the oldest backup that exists or + // ever existed in the chain. For all other backups, this is the version time + // of the backup. This field can be used to understand what data is being + // retained by the backup system. + google.protobuf.Timestamp oldest_version_time = 18 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The instance partition(s) storing the backup. + // + // This is the same as the list of the instance partition(s) that the database + // had footprint in at the backup's `version_time`. + repeated BackupInstancePartition instance_partitions = 19 + [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// The request for +// [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]. +message CreateBackupRequest { + // Required. The name of the instance in which the backup will be + // created. This must be the same instance that contains the database the + // backup will be created from. The backup will be stored in the + // location(s) specified in the instance configuration of this + // instance. Values are of the form + // `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Required. The id of the backup to be created. The `backup_id` appended to + // `parent` forms the full backup name of the form + // `projects//instances//backups/`. + string backup_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The backup to create. + Backup backup = 3 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The encryption configuration used to encrypt the backup. If this + // field is not specified, the backup will use the same encryption + // configuration as the database by default, namely + // [encryption_type][google.spanner.admin.database.v1.CreateBackupEncryptionConfig.encryption_type] + // = `USE_DATABASE_ENCRYPTION`. + CreateBackupEncryptionConfig encryption_config = 4 + [(google.api.field_behavior) = OPTIONAL]; +} + +// Metadata type for the operation returned by +// [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup]. +message CreateBackupMetadata { + // The name of the backup being created. + string name = 1 [ + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; + + // The name of the database the backup is created from. + string database = 2 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; + + // The progress of the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // operation. + OperationProgress progress = 3; + + // The time at which cancellation of this operation was received. + // [Operations.CancelOperation][google.longrunning.Operations.CancelOperation] + // starts asynchronous cancellation on a long-running operation. The server + // makes a best effort to cancel the operation, but success is not guaranteed. + // Clients can use + // [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + // other methods to check whether the cancellation succeeded or whether the + // operation completed despite cancellation. On successful cancellation, + // the operation is not deleted; instead, it becomes an operation with + // an [Operation.error][google.longrunning.Operation.error] value with a + // [google.rpc.Status.code][google.rpc.Status.code] of 1, + // corresponding to `Code.CANCELLED`. + google.protobuf.Timestamp cancel_time = 4; +} + +// The request for +// [CopyBackup][google.spanner.admin.database.v1.DatabaseAdmin.CopyBackup]. +message CopyBackupRequest { + // Required. The name of the destination instance that will contain the backup + // copy. Values are of the form: `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Required. The id of the backup copy. + // The `backup_id` appended to `parent` forms the full backup_uri of the form + // `projects//instances//backups/`. + string backup_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The source backup to be copied. + // The source backup needs to be in READY state for it to be copied. + // Once CopyBackup is in progress, the source backup cannot be deleted or + // cleaned up on expiration until CopyBackup is finished. + // Values are of the form: + // `projects//instances//backups/`. + string source_backup = 3 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; + + // Required. The expiration time of the backup in microsecond granularity. + // The expiration time must be at least 6 hours and at most 366 days + // from the `create_time` of the source backup. Once the `expire_time` has + // passed, the backup is eligible to be automatically deleted by Cloud Spanner + // to free the resources used by the backup. + google.protobuf.Timestamp expire_time = 4 + [(google.api.field_behavior) = REQUIRED]; + + // Optional. The encryption configuration used to encrypt the backup. If this + // field is not specified, the backup will use the same encryption + // configuration as the source backup by default, namely + // [encryption_type][google.spanner.admin.database.v1.CopyBackupEncryptionConfig.encryption_type] + // = `USE_CONFIG_DEFAULT_OR_BACKUP_ENCRYPTION`. + CopyBackupEncryptionConfig encryption_config = 5 + [(google.api.field_behavior) = OPTIONAL]; +} + +// Metadata type for the operation returned by +// [CopyBackup][google.spanner.admin.database.v1.DatabaseAdmin.CopyBackup]. +message CopyBackupMetadata { + // The name of the backup being created through the copy operation. + // Values are of the form + // `projects//instances//backups/`. + string name = 1 [ + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; + + // The name of the source backup that is being copied. + // Values are of the form + // `projects//instances//backups/`. + string source_backup = 2 [ + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; + + // The progress of the + // [CopyBackup][google.spanner.admin.database.v1.DatabaseAdmin.CopyBackup] + // operation. + OperationProgress progress = 3; + + // The time at which cancellation of CopyBackup operation was received. + // [Operations.CancelOperation][google.longrunning.Operations.CancelOperation] + // starts asynchronous cancellation on a long-running operation. The server + // makes a best effort to cancel the operation, but success is not guaranteed. + // Clients can use + // [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + // other methods to check whether the cancellation succeeded or whether the + // operation completed despite cancellation. On successful cancellation, + // the operation is not deleted; instead, it becomes an operation with + // an [Operation.error][google.longrunning.Operation.error] value with a + // [google.rpc.Status.code][google.rpc.Status.code] of 1, + // corresponding to `Code.CANCELLED`. + google.protobuf.Timestamp cancel_time = 4; +} + +// The request for +// [UpdateBackup][google.spanner.admin.database.v1.DatabaseAdmin.UpdateBackup]. +message UpdateBackupRequest { + // Required. The backup to update. `backup.name`, and the fields to be updated + // as specified by `update_mask` are required. Other fields are ignored. + // Update is only supported for the following fields: + // * `backup.expire_time`. + Backup backup = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. A mask specifying which fields (e.g. `expire_time`) in the + // Backup resource should be updated. This mask is relative to the Backup + // resource, not to the request message. The field mask must always be + // specified; this prevents any future fields from being erased accidentally + // by clients that do not know about them. + google.protobuf.FieldMask update_mask = 2 + [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [GetBackup][google.spanner.admin.database.v1.DatabaseAdmin.GetBackup]. +message GetBackupRequest { + // Required. Name of the backup. + // Values are of the form + // `projects//instances//backups/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; +} + +// The request for +// [DeleteBackup][google.spanner.admin.database.v1.DatabaseAdmin.DeleteBackup]. +message DeleteBackupRequest { + // Required. Name of the backup to delete. + // Values are of the form + // `projects//instances//backups/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; +} + +// The request for +// [ListBackups][google.spanner.admin.database.v1.DatabaseAdmin.ListBackups]. +message ListBackupsRequest { + // Required. The instance to list backups from. Values are of the + // form `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // An expression that filters the list of returned backups. + // + // A filter expression consists of a field name, a comparison operator, and a + // value for filtering. + // The value must be a string, a number, or a boolean. The comparison operator + // must be one of: `<`, `>`, `<=`, `>=`, `!=`, `=`, or `:`. + // Colon `:` is the contains operator. Filter rules are not case sensitive. + // + // The following fields in the + // [Backup][google.spanner.admin.database.v1.Backup] are eligible for + // filtering: + // + // * `name` + // * `database` + // * `state` + // * `create_time` (and values are of the format YYYY-MM-DDTHH:MM:SSZ) + // * `expire_time` (and values are of the format YYYY-MM-DDTHH:MM:SSZ) + // * `version_time` (and values are of the format YYYY-MM-DDTHH:MM:SSZ) + // * `size_bytes` + // * `backup_schedules` + // + // You can combine multiple expressions by enclosing each expression in + // parentheses. By default, expressions are combined with AND logic, but + // you can specify AND, OR, and NOT logic explicitly. + // + // Here are a few examples: + // + // * `name:Howl` - The backup's name contains the string "howl". + // * `database:prod` + // - The database's name contains the string "prod". + // * `state:CREATING` - The backup is pending creation. + // * `state:READY` - The backup is fully created and ready for use. + // * `(name:howl) AND (create_time < \"2018-03-28T14:50:00Z\")` + // - The backup name contains the string "howl" and `create_time` + // of the backup is before 2018-03-28T14:50:00Z. + // * `expire_time < \"2018-03-28T14:50:00Z\"` + // - The backup `expire_time` is before 2018-03-28T14:50:00Z. + // * `size_bytes > 10000000000` - The backup's size is greater than 10GB + // * `backup_schedules:daily` + // - The backup is created from a schedule with "daily" in its name. + string filter = 2; + + // Number of backups to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 3; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.database.v1.ListBackupsResponse.next_page_token] + // from a previous + // [ListBackupsResponse][google.spanner.admin.database.v1.ListBackupsResponse] + // to the same `parent` and with the same `filter`. + string page_token = 4; +} + +// The response for +// [ListBackups][google.spanner.admin.database.v1.DatabaseAdmin.ListBackups]. +message ListBackupsResponse { + // The list of matching backups. Backups returned are ordered by `create_time` + // in descending order, starting from the most recent `create_time`. + repeated Backup backups = 1; + + // `next_page_token` can be sent in a subsequent + // [ListBackups][google.spanner.admin.database.v1.DatabaseAdmin.ListBackups] + // call to fetch more of the matching backups. + string next_page_token = 2; +} + +// The request for +// [ListBackupOperations][google.spanner.admin.database.v1.DatabaseAdmin.ListBackupOperations]. +message ListBackupOperationsRequest { + // Required. The instance of the backup operations. Values are of + // the form `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // An expression that filters the list of returned backup operations. + // + // A filter expression consists of a field name, a + // comparison operator, and a value for filtering. + // The value must be a string, a number, or a boolean. The comparison operator + // must be one of: `<`, `>`, `<=`, `>=`, `!=`, `=`, or `:`. + // Colon `:` is the contains operator. Filter rules are not case sensitive. + // + // The following fields in the [operation][google.longrunning.Operation] + // are eligible for filtering: + // + // * `name` - The name of the long-running operation + // * `done` - False if the operation is in progress, else true. + // * `metadata.@type` - the type of metadata. For example, the type string + // for + // [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata] + // is + // `type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata`. + // * `metadata.` - any field in metadata.value. + // `metadata.@type` must be specified first if filtering on metadata + // fields. + // * `error` - Error associated with the long-running operation. + // * `response.@type` - the type of response. + // * `response.` - any field in response.value. + // + // You can combine multiple expressions by enclosing each expression in + // parentheses. By default, expressions are combined with AND logic, but + // you can specify AND, OR, and NOT logic explicitly. + // + // Here are a few examples: + // + // * `done:true` - The operation is complete. + // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + // `metadata.database:prod` - Returns operations where: + // * The operation's metadata type is + // [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. + // * The source database name of backup contains the string "prod". + // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + // `(metadata.name:howl) AND` \ + // `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ + // `(error:*)` - Returns operations where: + // * The operation's metadata type is + // [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. + // * The backup name contains the string "howl". + // * The operation started before 2018-03-28T14:50:00Z. + // * The operation resulted in an error. + // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CopyBackupMetadata) AND` \ + // `(metadata.source_backup:test) AND` \ + // `(metadata.progress.start_time < \"2022-01-18T14:50:00Z\") AND` \ + // `(error:*)` - Returns operations where: + // * The operation's metadata type is + // [CopyBackupMetadata][google.spanner.admin.database.v1.CopyBackupMetadata]. + // * The source backup name contains the string "test". + // * The operation started before 2022-01-18T14:50:00Z. + // * The operation resulted in an error. + // * `((metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CreateBackupMetadata) AND` \ + // `(metadata.database:test_db)) OR` \ + // `((metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.CopyBackupMetadata) + // AND` \ + // `(metadata.source_backup:test_bkp)) AND` \ + // `(error:*)` - Returns operations where: + // * The operation's metadata matches either of criteria: + // * The operation's metadata type is + // [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata] + // AND the source database name of the backup contains the string + // "test_db" + // * The operation's metadata type is + // [CopyBackupMetadata][google.spanner.admin.database.v1.CopyBackupMetadata] + // AND the source backup name contains the string "test_bkp" + // * The operation resulted in an error. + string filter = 2; + + // Number of operations to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 3; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.database.v1.ListBackupOperationsResponse.next_page_token] + // from a previous + // [ListBackupOperationsResponse][google.spanner.admin.database.v1.ListBackupOperationsResponse] + // to the same `parent` and with the same `filter`. + string page_token = 4; +} + +// The response for +// [ListBackupOperations][google.spanner.admin.database.v1.DatabaseAdmin.ListBackupOperations]. +message ListBackupOperationsResponse { + // The list of matching backup [long-running + // operations][google.longrunning.Operation]. Each operation's name will be + // prefixed by the backup's name. The operation's + // [metadata][google.longrunning.Operation.metadata] field type + // `metadata.type_url` describes the type of the metadata. Operations returned + // include those that are pending or have completed/failed/canceled within the + // last 7 days. Operations returned are ordered by + // `operation.metadata.value.progress.start_time` in descending order starting + // from the most recently started operation. + repeated google.longrunning.Operation operations = 1; + + // `next_page_token` can be sent in a subsequent + // [ListBackupOperations][google.spanner.admin.database.v1.DatabaseAdmin.ListBackupOperations] + // call to fetch more of the matching metadata. + string next_page_token = 2; +} + +// Information about a backup. +message BackupInfo { + // Name of the backup. + string backup = 1 [ + (google.api.resource_reference) = { type: "spanner.googleapis.com/Backup" } + ]; + + // The backup contains an externally consistent copy of `source_database` at + // the timestamp specified by `version_time`. If the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // request did not specify `version_time`, the `version_time` of the backup is + // equivalent to the `create_time`. + google.protobuf.Timestamp version_time = 4; + + // The time the + // [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup] + // request was received. + google.protobuf.Timestamp create_time = 2; + + // Name of the database the backup was created from. + string source_database = 3 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; +} + +// Encryption configuration for the backup to create. +message CreateBackupEncryptionConfig { + // Encryption types for the backup. + enum EncryptionType { + // Unspecified. Do not use. + ENCRYPTION_TYPE_UNSPECIFIED = 0; + + // Use the same encryption configuration as the database. This is the + // default option when + // [encryption_config][google.spanner.admin.database.v1.CreateBackupEncryptionConfig] + // is empty. For example, if the database is using + // `Customer_Managed_Encryption`, the backup will be using the same Cloud + // KMS key as the database. + USE_DATABASE_ENCRYPTION = 1; + + // Use Google default encryption. + GOOGLE_DEFAULT_ENCRYPTION = 2; + + // Use customer managed encryption. If specified, `kms_key_name` + // must contain a valid Cloud KMS key. + CUSTOMER_MANAGED_ENCRYPTION = 3; + } + + // Required. The encryption type of the backup. + EncryptionType encryption_type = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The Cloud KMS key that will be used to protect the backup. + // This field should be set only when + // [encryption_type][google.spanner.admin.database.v1.CreateBackupEncryptionConfig.encryption_type] + // is `CUSTOMER_MANAGED_ENCRYPTION`. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + string kms_key_name = 2 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + } + ]; + + // Optional. Specifies the KMS configuration for the one or more keys used to + // protect the backup. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + // + // The keys referenced by kms_key_names must fully cover all + // regions of the backup's instance configuration. Some examples: + // * For single region instance configs, specify a single regional + // location KMS key. + // * For multi-regional instance configs of type GOOGLE_MANAGED, + // either specify a multi-regional location KMS key or multiple regional + // location KMS keys that cover all regions in the instance config. + // * For an instance config of type USER_MANAGED, please specify only + // regional location KMS keys to cover each region in the instance config. + // Multi-regional location KMS keys are not supported for USER_MANAGED + // instance configs. + repeated string kms_key_names = 3 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + } + ]; +} + +// Encryption configuration for the copied backup. +message CopyBackupEncryptionConfig { + // Encryption types for the backup. + enum EncryptionType { + // Unspecified. Do not use. + ENCRYPTION_TYPE_UNSPECIFIED = 0; + + // This is the default option for + // [CopyBackup][google.spanner.admin.database.v1.DatabaseAdmin.CopyBackup] + // when + // [encryption_config][google.spanner.admin.database.v1.CopyBackupEncryptionConfig] + // is not specified. For example, if the source backup is using + // `Customer_Managed_Encryption`, the backup will be using the same Cloud + // KMS key as the source backup. + USE_CONFIG_DEFAULT_OR_BACKUP_ENCRYPTION = 1; + + // Use Google default encryption. + GOOGLE_DEFAULT_ENCRYPTION = 2; + + // Use customer managed encryption. If specified, either `kms_key_name` or + // `kms_key_names` must contain valid Cloud KMS key(s). + CUSTOMER_MANAGED_ENCRYPTION = 3; + } + + // Required. The encryption type of the backup. + EncryptionType encryption_type = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The Cloud KMS key that will be used to protect the backup. + // This field should be set only when + // [encryption_type][google.spanner.admin.database.v1.CopyBackupEncryptionConfig.encryption_type] + // is `CUSTOMER_MANAGED_ENCRYPTION`. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + string kms_key_name = 2 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + } + ]; + + // Optional. Specifies the KMS configuration for the one or more keys used to + // protect the backup. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + // Kms keys specified can be in any order. + // + // The keys referenced by kms_key_names must fully cover all + // regions of the backup's instance configuration. Some examples: + // * For single region instance configs, specify a single regional + // location KMS key. + // * For multi-regional instance configs of type GOOGLE_MANAGED, + // either specify a multi-regional location KMS key or multiple regional + // location KMS keys that cover all regions in the instance config. + // * For an instance config of type USER_MANAGED, please specify only + // regional location KMS keys to cover each region in the instance config. + // Multi-regional location KMS keys are not supported for USER_MANAGED + // instance configs. + repeated string kms_key_names = 3 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + } + ]; +} + +// The specification for full backups. +// A full backup stores the entire contents of the database at a given +// version time. +message FullBackupSpec {} + +// The specification for incremental backup chains. +// An incremental backup stores the delta of changes between a previous +// backup and the database contents at a given version time. An +// incremental backup chain consists of a full backup and zero or more +// successive incremental backups. The first backup created for an +// incremental backup chain is always a full backup. +message IncrementalBackupSpec {} + +// Instance partition information for the backup. +message BackupInstancePartition { + // A unique identifier for the instance partition. Values are of the form + // `projects//instances//instancePartitions/` + string instance_partition = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/InstancePartition" + }]; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/backup_schedule.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/backup_schedule.proto new file mode 100644 index 00000000..c273516a --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/backup_schedule.proto @@ -0,0 +1,230 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.admin.database.v1; + +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/spanner/admin/database/v1/backup.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Admin.Database.V1"; +option go_package = "cloud.google.com/go/spanner/admin/database/apiv1/databasepb;databasepb"; +option java_multiple_files = true; +option java_outer_classname = "BackupScheduleProto"; +option java_package = "com.google.spanner.admin.database.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Database\\V1"; +option ruby_package = "Google::Cloud::Spanner::Admin::Database::V1"; + +// Defines specifications of the backup schedule. +message BackupScheduleSpec { + // Required. + oneof schedule_spec { + // Cron style schedule specification. + CrontabSpec cron_spec = 1; + } +} + +// BackupSchedule expresses the automated backup creation specification for a +// Spanner database. +// Next ID: 10 +message BackupSchedule { + option (google.api.resource) = { + type: "spanner.googleapis.com/BackupSchedule" + pattern: "projects/{project}/instances/{instance}/databases/{database}/backupSchedules/{schedule}" + plural: "backupSchedules" + singular: "backupSchedule" + }; + + // Identifier. Output only for the + // [CreateBackupSchedule][DatabaseAdmin.CreateBackupSchededule] operation. + // Required for the + // [UpdateBackupSchedule][google.spanner.admin.database.v1.DatabaseAdmin.UpdateBackupSchedule] + // operation. A globally unique identifier for the backup schedule which + // cannot be changed. Values are of the form + // `projects//instances//databases//backupSchedules/[a-z][a-z0-9_\-]*[a-z0-9]` + // The final segment of the name must be between 2 and 60 characters in + // length. + string name = 1 [(google.api.field_behavior) = IDENTIFIER]; + + // Optional. The schedule specification based on which the backup creations + // are triggered. + BackupScheduleSpec spec = 6 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The retention duration of a backup that must be at least 6 hours + // and at most 366 days. The backup is eligible to be automatically deleted + // once the retention period has elapsed. + google.protobuf.Duration retention_duration = 3 + [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The encryption configuration that will be used to encrypt the + // backup. If this field is not specified, the backup will use the same + // encryption configuration as the database. + CreateBackupEncryptionConfig encryption_config = 4 + [(google.api.field_behavior) = OPTIONAL]; + + // Required. Backup type spec determines the type of backup that is created by + // the backup schedule. Currently, only full backups are supported. + oneof backup_type_spec { + // The schedule creates only full backups. + FullBackupSpec full_backup_spec = 7; + + // The schedule creates incremental backup chains. + IncrementalBackupSpec incremental_backup_spec = 8; + } + + // Output only. The timestamp at which the schedule was last updated. + // If the schedule has never been updated, this field contains the timestamp + // when the schedule was first created. + google.protobuf.Timestamp update_time = 9 + [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// CrontabSpec can be used to specify the version time and frequency at +// which the backup should be created. +message CrontabSpec { + // Required. Textual representation of the crontab. User can customize the + // backup frequency and the backup version time using the cron + // expression. The version time must be in UTC timezone. + // + // The backup will contain an externally consistent copy of the + // database at the version time. Allowed frequencies are 12 hour, 1 day, + // 1 week and 1 month. Examples of valid cron specifications: + // * `0 2/12 * * * ` : every 12 hours at (2, 14) hours past midnight in UTC. + // * `0 2,14 * * * ` : every 12 hours at (2,14) hours past midnight in UTC. + // * `0 2 * * * ` : once a day at 2 past midnight in UTC. + // * `0 2 * * 0 ` : once a week every Sunday at 2 past midnight in UTC. + // * `0 2 8 * * ` : once a month on 8th day at 2 past midnight in UTC. + string text = 1 [(google.api.field_behavior) = REQUIRED]; + + // Output only. The time zone of the times in `CrontabSpec.text`. Currently + // only UTC is supported. + string time_zone = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Schedule backups will contain an externally consistent copy + // of the database at the version time specified in + // `schedule_spec.cron_spec`. However, Spanner may not initiate the creation + // of the scheduled backups at that version time. Spanner will initiate + // the creation of scheduled backups within the time window bounded by the + // version_time specified in `schedule_spec.cron_spec` and version_time + + // `creation_window`. + google.protobuf.Duration creation_window = 3 + [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// The request for +// [CreateBackupSchedule][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackupSchedule]. +message CreateBackupScheduleRequest { + // Required. The name of the database that this backup schedule applies to. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Required. The Id to use for the backup schedule. The `backup_schedule_id` + // appended to `parent` forms the full backup schedule name of the form + // `projects//instances//databases//backupSchedules/`. + string backup_schedule_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The backup schedule to create. + BackupSchedule backup_schedule = 3 [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [GetBackupSchedule][google.spanner.admin.database.v1.DatabaseAdmin.GetBackupSchedule]. +message GetBackupScheduleRequest { + // Required. The name of the schedule to retrieve. + // Values are of the form + // `projects//instances//databases//backupSchedules/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/BackupSchedule" + } + ]; +} + +// The request for +// [DeleteBackupSchedule][google.spanner.admin.database.v1.DatabaseAdmin.DeleteBackupSchedule]. +message DeleteBackupScheduleRequest { + // Required. The name of the schedule to delete. + // Values are of the form + // `projects//instances//databases//backupSchedules/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/BackupSchedule" + } + ]; +} + +// The request for +// [ListBackupSchedules][google.spanner.admin.database.v1.DatabaseAdmin.ListBackupSchedules]. +message ListBackupSchedulesRequest { + // Required. Database is the parent resource whose backup schedules should be + // listed. Values are of the form + // projects//instances//databases/ + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Optional. Number of backup schedules to be returned in the response. If 0 + // or less, defaults to the server's maximum allowed page size. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.database.v1.ListBackupSchedulesResponse.next_page_token] + // from a previous + // [ListBackupSchedulesResponse][google.spanner.admin.database.v1.ListBackupSchedulesResponse] + // to the same `parent`. + string page_token = 4 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for +// [ListBackupSchedules][google.spanner.admin.database.v1.DatabaseAdmin.ListBackupSchedules]. +message ListBackupSchedulesResponse { + // The list of backup schedules for a database. + repeated BackupSchedule backup_schedules = 1; + + // `next_page_token` can be sent in a subsequent + // [ListBackupSchedules][google.spanner.admin.database.v1.DatabaseAdmin.ListBackupSchedules] + // call to fetch more of the schedules. + string next_page_token = 2; +} + +// The request for +// [UpdateBackupScheduleRequest][google.spanner.admin.database.v1.DatabaseAdmin.UpdateBackupSchedule]. +message UpdateBackupScheduleRequest { + // Required. The backup schedule to update. `backup_schedule.name`, and the + // fields to be updated as specified by `update_mask` are required. Other + // fields are ignored. + BackupSchedule backup_schedule = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. A mask specifying which fields in the BackupSchedule resource + // should be updated. This mask is relative to the BackupSchedule resource, + // not to the request message. The field mask must always be + // specified; this prevents any future fields from being erased + // accidentally. + google.protobuf.FieldMask update_mask = 2 + [(google.api.field_behavior) = REQUIRED]; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/common.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/common.proto new file mode 100644 index 00000000..c494b8cf --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/common.proto @@ -0,0 +1,132 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.admin.database.v1; + +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Admin.Database.V1"; +option go_package = "cloud.google.com/go/spanner/admin/database/apiv1/databasepb;databasepb"; +option java_multiple_files = true; +option java_outer_classname = "CommonProto"; +option java_package = "com.google.spanner.admin.database.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Database\\V1"; +option ruby_package = "Google::Cloud::Spanner::Admin::Database::V1"; +option (google.api.resource_definition) = { + type: "cloudkms.googleapis.com/CryptoKey" + pattern: "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}" +}; +option (google.api.resource_definition) = { + type: "cloudkms.googleapis.com/CryptoKeyVersion" + pattern: "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVersions/{crypto_key_version}" +}; + +// Encapsulates progress related information for a Cloud Spanner long +// running operation. +message OperationProgress { + // Percent completion of the operation. + // Values are between 0 and 100 inclusive. + int32 progress_percent = 1; + + // Time the request was received. + google.protobuf.Timestamp start_time = 2; + + // If set, the time at which this operation failed or was completed + // successfully. + google.protobuf.Timestamp end_time = 3; +} + +// Encryption configuration for a Cloud Spanner database. +message EncryptionConfig { + // The Cloud KMS key to be used for encrypting and decrypting + // the database. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + string kms_key_name = 2 [(google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + }]; + + // Specifies the KMS configuration for the one or more keys used to encrypt + // the database. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + // + // The keys referenced by kms_key_names must fully cover all + // regions of the database instance configuration. Some examples: + // * For single region database instance configs, specify a single regional + // location KMS key. + // * For multi-regional database instance configs of type GOOGLE_MANAGED, + // either specify a multi-regional location KMS key or multiple regional + // location KMS keys that cover all regions in the instance config. + // * For a database instance config of type USER_MANAGED, please specify only + // regional location KMS keys to cover each region in the instance config. + // Multi-regional location KMS keys are not supported for USER_MANAGED + // instance configs. + repeated string kms_key_names = 3 [(google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + }]; +} + +// Encryption information for a Cloud Spanner database or backup. +message EncryptionInfo { + // Possible encryption types. + enum Type { + // Encryption type was not specified, though data at rest remains encrypted. + TYPE_UNSPECIFIED = 0; + + // The data is encrypted at rest with a key that is + // fully managed by Google. No key version or status will be populated. + // This is the default state. + GOOGLE_DEFAULT_ENCRYPTION = 1; + + // The data is encrypted at rest with a key that is + // managed by the customer. The active version of the key. `kms_key_version` + // will be populated, and `encryption_status` may be populated. + CUSTOMER_MANAGED_ENCRYPTION = 2; + } + + // Output only. The type of encryption. + Type encryption_type = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. If present, the status of a recent encrypt/decrypt call on + // underlying data for this database or backup. Regardless of status, data is + // always encrypted at rest. + google.rpc.Status encryption_status = 4 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. A Cloud KMS key version that is being used to protect the + // database or backup. + string kms_key_version = 2 [ + (google.api.field_behavior) = OUTPUT_ONLY, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKeyVersion" + } + ]; +} + +// Indicates the dialect type of a database. +enum DatabaseDialect { + // Default value. This value will create a database with the + // GOOGLE_STANDARD_SQL dialect. + DATABASE_DIALECT_UNSPECIFIED = 0; + + // GoogleSQL supported SQL. + GOOGLE_STANDARD_SQL = 1; + + // PostgreSQL supported SQL. + POSTGRESQL = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner.yaml new file mode 100644 index 00000000..9d85286a --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner.yaml @@ -0,0 +1,69 @@ +type: google.api.Service +config_version: 3 +name: spanner.googleapis.com +title: Cloud Spanner API + +apis: +- name: google.longrunning.Operations +- name: google.spanner.admin.database.v1.DatabaseAdmin + +types: +- name: google.spanner.admin.database.v1.CopyBackupMetadata +- name: google.spanner.admin.database.v1.CreateBackupMetadata +- name: google.spanner.admin.database.v1.CreateDatabaseMetadata +- name: google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata +- name: google.spanner.admin.database.v1.RestoreDatabaseMetadata +- name: google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata +- name: google.spanner.admin.database.v1.UpdateDatabaseMetadata + +documentation: + summary: |- + Cloud Spanner is a managed, mission-critical, globally consistent and + scalable relational database service. + +backend: + rules: + - selector: 'google.longrunning.Operations.*' + deadline: 3600.0 + - selector: 'google.spanner.admin.database.v1.DatabaseAdmin.*' + deadline: 3600.0 + +http: + rules: + - selector: google.longrunning.Operations.CancelOperation + post: '/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel' + additional_bindings: + - post: '/v1/{name=projects/*/instances/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instances/*/backups/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instanceConfigs/*/operations/*}:cancel' + - selector: google.longrunning.Operations.DeleteOperation + delete: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - delete: '/v1/{name=projects/*/instances/*/operations/*}' + - delete: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - delete: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - selector: google.longrunning.Operations.GetOperation + get: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations/*}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - selector: google.longrunning.Operations.ListOperations + get: '/v1/{name=projects/*/instances/*/databases/*/operations}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations}' + +authentication: + rules: + - selector: 'google.longrunning.Operations.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.admin + - selector: 'google.spanner.admin.database.v1.DatabaseAdmin.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.admin diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_admin_database_grpc_service_config.json b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_admin_database_grpc_service_config.json new file mode 100755 index 00000000..f6d532ec --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_admin_database_grpc_service_config.json @@ -0,0 +1,146 @@ +{ + "methodConfig": [ + { + "name": [ + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "ListDatabases" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "GetDatabase" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "UpdateDatabase" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "UpdateDatabaseDdl" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "DropDatabase" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "GetDatabaseDdl" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "GetBackup" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "ListBackups" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "DeleteBackup" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "UpdateBackup" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "ListDatabaseOperations" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "ListBackupOperations" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "ListDatabaseRoles" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "CreateBackupSchedule" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "GetBackupSchedule" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "UpdateBackupSchedule" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "DeleteBackupSchedule" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "ListBackupSchedules" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "AddSplitPoints" + } + ], + "timeout": "3600s", + "retryPolicy": { + "initialBackoff": "1s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "DEADLINE_EXCEEDED" + ] + } + }, + { + "name": [ + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "GetIamPolicy" + } + ], + "timeout": "30s", + "retryPolicy": { + "initialBackoff": "1s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "DEADLINE_EXCEEDED" + ] + } + }, + { + "name": [ + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "CreateDatabase" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "CreateBackup" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "CopyBackup" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "RestoreDatabase" + } + ], + "timeout": "3600s" + }, + { + "name": [ + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "SetIamPolicy" + }, + { + "service": "google.spanner.admin.database.v1.DatabaseAdmin", + "method": "TestIamPermissions" + } + ], + "timeout": "30s" + } + ] +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_database_admin.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_database_admin.proto new file mode 100644 index 00000000..36e06f1e --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_database_admin.proto @@ -0,0 +1,1284 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.admin.database.v1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/iam/v1/iam_policy.proto"; +import "google/iam/v1/policy.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/spanner/admin/database/v1/backup.proto"; +import "google/spanner/admin/database/v1/backup_schedule.proto"; +import "google/spanner/admin/database/v1/common.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Admin.Database.V1"; +option go_package = "cloud.google.com/go/spanner/admin/database/apiv1/databasepb;databasepb"; +option java_multiple_files = true; +option java_outer_classname = "SpannerDatabaseAdminProto"; +option java_package = "com.google.spanner.admin.database.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Database\\V1"; +option ruby_package = "Google::Cloud::Spanner::Admin::Database::V1"; +option (google.api.resource_definition) = { + type: "spanner.googleapis.com/Instance" + pattern: "projects/{project}/instances/{instance}" +}; +option (google.api.resource_definition) = { + type: "spanner.googleapis.com/InstancePartition" + pattern: "projects/{project}/instances/{instance}/instancePartitions/{instance_partition}" +}; + +// Cloud Spanner Database Admin API +// +// The Cloud Spanner Database Admin API can be used to: +// * create, drop, and list databases +// * update the schema of pre-existing databases +// * create, delete, copy and list backups for a database +// * restore a database from an existing backup +service DatabaseAdmin { + option (google.api.default_host) = "spanner.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/spanner.admin"; + + // Lists Cloud Spanner databases. + rpc ListDatabases(ListDatabasesRequest) returns (ListDatabasesResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*}/databases" + }; + option (google.api.method_signature) = "parent"; + } + + // Creates a new Cloud Spanner database and starts to prepare it for serving. + // The returned [long-running operation][google.longrunning.Operation] will + // have a name of the format `/operations/` and + // can be used to track preparation of the database. The + // [metadata][google.longrunning.Operation.metadata] field type is + // [CreateDatabaseMetadata][google.spanner.admin.database.v1.CreateDatabaseMetadata]. + // The [response][google.longrunning.Operation.response] field type is + // [Database][google.spanner.admin.database.v1.Database], if successful. + rpc CreateDatabase(CreateDatabaseRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*}/databases" + body: "*" + }; + option (google.api.method_signature) = "parent,create_statement"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.database.v1.Database" + metadata_type: "google.spanner.admin.database.v1.CreateDatabaseMetadata" + }; + } + + // Gets the state of a Cloud Spanner database. + rpc GetDatabase(GetDatabaseRequest) returns (Database) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instances/*/databases/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Updates a Cloud Spanner database. The returned + // [long-running operation][google.longrunning.Operation] can be used to track + // the progress of updating the database. If the named database does not + // exist, returns `NOT_FOUND`. + // + // While the operation is pending: + // + // * The database's + // [reconciling][google.spanner.admin.database.v1.Database.reconciling] + // field is set to true. + // * Cancelling the operation is best-effort. If the cancellation succeeds, + // the operation metadata's + // [cancel_time][google.spanner.admin.database.v1.UpdateDatabaseMetadata.cancel_time] + // is set, the updates are reverted, and the operation terminates with a + // `CANCELLED` status. + // * New UpdateDatabase requests will return a `FAILED_PRECONDITION` error + // until the pending operation is done (returns successfully or with + // error). + // * Reading the database via the API continues to give the pre-request + // values. + // + // Upon completion of the returned operation: + // + // * The new values are in effect and readable via the API. + // * The database's + // [reconciling][google.spanner.admin.database.v1.Database.reconciling] + // field becomes false. + // + // The returned [long-running operation][google.longrunning.Operation] will + // have a name of the format + // `projects//instances//databases//operations/` + // and can be used to track the database modification. The + // [metadata][google.longrunning.Operation.metadata] field type is + // [UpdateDatabaseMetadata][google.spanner.admin.database.v1.UpdateDatabaseMetadata]. + // The [response][google.longrunning.Operation.response] field type is + // [Database][google.spanner.admin.database.v1.Database], if successful. + rpc UpdateDatabase(UpdateDatabaseRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + patch: "/v1/{database.name=projects/*/instances/*/databases/*}" + body: "database" + }; + option (google.api.method_signature) = "database,update_mask"; + option (google.longrunning.operation_info) = { + response_type: "Database" + metadata_type: "UpdateDatabaseMetadata" + }; + } + + // Updates the schema of a Cloud Spanner database by + // creating/altering/dropping tables, columns, indexes, etc. The returned + // [long-running operation][google.longrunning.Operation] will have a name of + // the format `/operations/` and can be used to + // track execution of the schema change(s). The + // [metadata][google.longrunning.Operation.metadata] field type is + // [UpdateDatabaseDdlMetadata][google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata]. + // The operation has no response. + rpc UpdateDatabaseDdl(UpdateDatabaseDdlRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + patch: "/v1/{database=projects/*/instances/*/databases/*}/ddl" + body: "*" + }; + option (google.api.method_signature) = "database,statements"; + option (google.longrunning.operation_info) = { + response_type: "google.protobuf.Empty" + metadata_type: "google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata" + }; + } + + // Drops (aka deletes) a Cloud Spanner database. + // Completed backups for the database will be retained according to their + // `expire_time`. + // Note: Cloud Spanner might continue to accept requests for a few seconds + // after the database has been deleted. + rpc DropDatabase(DropDatabaseRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{database=projects/*/instances/*/databases/*}" + }; + option (google.api.method_signature) = "database"; + } + + // Returns the schema of a Cloud Spanner database as a list of formatted + // DDL statements. This method does not show pending schema updates, those may + // be queried using the [Operations][google.longrunning.Operations] API. + rpc GetDatabaseDdl(GetDatabaseDdlRequest) returns (GetDatabaseDdlResponse) { + option (google.api.http) = { + get: "/v1/{database=projects/*/instances/*/databases/*}/ddl" + }; + option (google.api.method_signature) = "database"; + } + + // Sets the access control policy on a database or backup resource. + // Replaces any existing policy. + // + // Authorization requires `spanner.databases.setIamPolicy` + // permission on [resource][google.iam.v1.SetIamPolicyRequest.resource]. + // For backups, authorization requires `spanner.backups.setIamPolicy` + // permission on [resource][google.iam.v1.SetIamPolicyRequest.resource]. + rpc SetIamPolicy(google.iam.v1.SetIamPolicyRequest) + returns (google.iam.v1.Policy) { + option (google.api.http) = { + post: "/v1/{resource=projects/*/instances/*/databases/*}:setIamPolicy" + body: "*" + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/backups/*}:setIamPolicy" + body: "*" + } + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/databases/*/backupSchedules/*}:setIamPolicy" + body: "*" + } + }; + option (google.api.method_signature) = "resource,policy"; + } + + // Gets the access control policy for a database or backup resource. + // Returns an empty policy if a database or backup exists but does not have a + // policy set. + // + // Authorization requires `spanner.databases.getIamPolicy` permission on + // [resource][google.iam.v1.GetIamPolicyRequest.resource]. + // For backups, authorization requires `spanner.backups.getIamPolicy` + // permission on [resource][google.iam.v1.GetIamPolicyRequest.resource]. + rpc GetIamPolicy(google.iam.v1.GetIamPolicyRequest) + returns (google.iam.v1.Policy) { + option (google.api.http) = { + post: "/v1/{resource=projects/*/instances/*/databases/*}:getIamPolicy" + body: "*" + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/backups/*}:getIamPolicy" + body: "*" + } + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/databases/*/backupSchedules/*}:getIamPolicy" + body: "*" + } + }; + option (google.api.method_signature) = "resource"; + } + + // Returns permissions that the caller has on the specified database or backup + // resource. + // + // Attempting this RPC on a non-existent Cloud Spanner database will + // result in a NOT_FOUND error if the user has + // `spanner.databases.list` permission on the containing Cloud + // Spanner instance. Otherwise returns an empty set of permissions. + // Calling this method on a backup that does not exist will + // result in a NOT_FOUND error if the user has + // `spanner.backups.list` permission on the containing instance. + rpc TestIamPermissions(google.iam.v1.TestIamPermissionsRequest) + returns (google.iam.v1.TestIamPermissionsResponse) { + option (google.api.http) = { + post: "/v1/{resource=projects/*/instances/*/databases/*}:testIamPermissions" + body: "*" + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/backups/*}:testIamPermissions" + body: "*" + } + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/databases/*/backupSchedules/*}:testIamPermissions" + body: "*" + } + additional_bindings { + post: "/v1/{resource=projects/*/instances/*/databases/*/databaseRoles/*}:testIamPermissions" + body: "*" + } + }; + option (google.api.method_signature) = "resource,permissions"; + } + + // Starts creating a new Cloud Spanner Backup. + // The returned backup [long-running operation][google.longrunning.Operation] + // will have a name of the format + // `projects//instances//backups//operations/` + // and can be used to track creation of the backup. The + // [metadata][google.longrunning.Operation.metadata] field type is + // [CreateBackupMetadata][google.spanner.admin.database.v1.CreateBackupMetadata]. + // The [response][google.longrunning.Operation.response] field type is + // [Backup][google.spanner.admin.database.v1.Backup], if successful. + // Cancelling the returned operation will stop the creation and delete the + // backup. There can be only one pending backup creation per database. Backup + // creation of different databases can run concurrently. + rpc CreateBackup(CreateBackupRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*}/backups" + body: "backup" + }; + option (google.api.method_signature) = "parent,backup,backup_id"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.database.v1.Backup" + metadata_type: "google.spanner.admin.database.v1.CreateBackupMetadata" + }; + } + + // Starts copying a Cloud Spanner Backup. + // The returned backup [long-running operation][google.longrunning.Operation] + // will have a name of the format + // `projects//instances//backups//operations/` + // and can be used to track copying of the backup. The operation is associated + // with the destination backup. + // The [metadata][google.longrunning.Operation.metadata] field type is + // [CopyBackupMetadata][google.spanner.admin.database.v1.CopyBackupMetadata]. + // The [response][google.longrunning.Operation.response] field type is + // [Backup][google.spanner.admin.database.v1.Backup], if successful. + // Cancelling the returned operation will stop the copying and delete the + // destination backup. Concurrent CopyBackup requests can run on the same + // source backup. + rpc CopyBackup(CopyBackupRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*}/backups:copy" + body: "*" + }; + option (google.api.method_signature) = + "parent,backup_id,source_backup,expire_time"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.database.v1.Backup" + metadata_type: "google.spanner.admin.database.v1.CopyBackupMetadata" + }; + } + + // Gets metadata on a pending or completed + // [Backup][google.spanner.admin.database.v1.Backup]. + rpc GetBackup(GetBackupRequest) returns (Backup) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instances/*/backups/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Updates a pending or completed + // [Backup][google.spanner.admin.database.v1.Backup]. + rpc UpdateBackup(UpdateBackupRequest) returns (Backup) { + option (google.api.http) = { + patch: "/v1/{backup.name=projects/*/instances/*/backups/*}" + body: "backup" + }; + option (google.api.method_signature) = "backup,update_mask"; + } + + // Deletes a pending or completed + // [Backup][google.spanner.admin.database.v1.Backup]. + rpc DeleteBackup(DeleteBackupRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/instances/*/backups/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Lists completed and pending backups. + // Backups returned are ordered by `create_time` in descending order, + // starting from the most recent `create_time`. + rpc ListBackups(ListBackupsRequest) returns (ListBackupsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*}/backups" + }; + option (google.api.method_signature) = "parent"; + } + + // Create a new database by restoring from a completed backup. The new + // database must be in the same project and in an instance with the same + // instance configuration as the instance containing + // the backup. The returned database [long-running + // operation][google.longrunning.Operation] has a name of the format + // `projects//instances//databases//operations/`, + // and can be used to track the progress of the operation, and to cancel it. + // The [metadata][google.longrunning.Operation.metadata] field type is + // [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. + // The [response][google.longrunning.Operation.response] type + // is [Database][google.spanner.admin.database.v1.Database], if + // successful. Cancelling the returned operation will stop the restore and + // delete the database. + // There can be only one database being restored into an instance at a time. + // Once the restore operation completes, a new restore operation can be + // initiated, without waiting for the optimize operation associated with the + // first restore to complete. + rpc RestoreDatabase(RestoreDatabaseRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*}/databases:restore" + body: "*" + }; + option (google.api.method_signature) = "parent,database_id,backup"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.database.v1.Database" + metadata_type: "google.spanner.admin.database.v1.RestoreDatabaseMetadata" + }; + } + + // Lists database [longrunning-operations][google.longrunning.Operation]. + // A database operation has a name of the form + // `projects//instances//databases//operations/`. + // The long-running operation + // [metadata][google.longrunning.Operation.metadata] field type + // `metadata.type_url` describes the type of the metadata. Operations returned + // include those that have completed/failed/canceled within the last 7 days, + // and pending operations. + rpc ListDatabaseOperations(ListDatabaseOperationsRequest) + returns (ListDatabaseOperationsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*}/databaseOperations" + }; + option (google.api.method_signature) = "parent"; + } + + // Lists the backup [long-running operations][google.longrunning.Operation] in + // the given instance. A backup operation has a name of the form + // `projects//instances//backups//operations/`. + // The long-running operation + // [metadata][google.longrunning.Operation.metadata] field type + // `metadata.type_url` describes the type of the metadata. Operations returned + // include those that have completed/failed/canceled within the last 7 days, + // and pending operations. Operations returned are ordered by + // `operation.metadata.value.progress.start_time` in descending order starting + // from the most recently started operation. + rpc ListBackupOperations(ListBackupOperationsRequest) + returns (ListBackupOperationsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*}/backupOperations" + }; + option (google.api.method_signature) = "parent"; + } + + // Lists Cloud Spanner database roles. + rpc ListDatabaseRoles(ListDatabaseRolesRequest) + returns (ListDatabaseRolesResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*/databases/*}/databaseRoles" + }; + option (google.api.method_signature) = "parent"; + } + + // Adds split points to specified tables, indexes of a database. + rpc AddSplitPoints(AddSplitPointsRequest) returns (AddSplitPointsResponse) { + option (google.api.http) = { + post: "/v1/{database=projects/*/instances/*/databases/*}:addSplitPoints" + body: "*" + }; + option (google.api.method_signature) = "database,split_points"; + } + + // Creates a new backup schedule. + rpc CreateBackupSchedule(CreateBackupScheduleRequest) + returns (BackupSchedule) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*/databases/*}/backupSchedules" + body: "backup_schedule" + }; + option (google.api.method_signature) = + "parent,backup_schedule,backup_schedule_id"; + } + + // Gets backup schedule for the input schedule name. + rpc GetBackupSchedule(GetBackupScheduleRequest) returns (BackupSchedule) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instances/*/databases/*/backupSchedules/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Updates a backup schedule. + rpc UpdateBackupSchedule(UpdateBackupScheduleRequest) + returns (BackupSchedule) { + option (google.api.http) = { + patch: "/v1/{backup_schedule.name=projects/*/instances/*/databases/*/backupSchedules/*}" + body: "backup_schedule" + }; + option (google.api.method_signature) = "backup_schedule,update_mask"; + } + + // Deletes a backup schedule. + rpc DeleteBackupSchedule(DeleteBackupScheduleRequest) + returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/instances/*/databases/*/backupSchedules/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Lists all the backup schedules for the database. + rpc ListBackupSchedules(ListBackupSchedulesRequest) + returns (ListBackupSchedulesResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*/databases/*}/backupSchedules" + }; + option (google.api.method_signature) = "parent"; + } +} + +// Information about the database restore. +message RestoreInfo { + // The type of the restore source. + RestoreSourceType source_type = 1; + + // Information about the source used to restore the database. + oneof source_info { + // Information about the backup used to restore the database. The backup + // may no longer exist. + BackupInfo backup_info = 2; + } +} + +// A Cloud Spanner database. +message Database { + option (google.api.resource) = { + type: "spanner.googleapis.com/Database" + pattern: "projects/{project}/instances/{instance}/databases/{database}" + }; + + // Indicates the current state of the database. + enum State { + // Not specified. + STATE_UNSPECIFIED = 0; + + // The database is still being created. Operations on the database may fail + // with `FAILED_PRECONDITION` in this state. + CREATING = 1; + + // The database is fully created and ready for use. + READY = 2; + + // The database is fully created and ready for use, but is still + // being optimized for performance and cannot handle full load. + // + // In this state, the database still references the backup + // it was restore from, preventing the backup + // from being deleted. When optimizations are complete, the full performance + // of the database will be restored, and the database will transition to + // `READY` state. + READY_OPTIMIZING = 3; + } + + // Required. The name of the database. Values are of the form + // `projects//instances//databases/`, + // where `` is as specified in the `CREATE DATABASE` + // statement. This name can be passed to other API methods to + // identify the database. + string name = 1 [(google.api.field_behavior) = REQUIRED]; + + // Output only. The current database state. + State state = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. If exists, the time at which the database creation started. + google.protobuf.Timestamp create_time = 3 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Applicable only for restored databases. Contains information + // about the restore source. + RestoreInfo restore_info = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. For databases that are using customer managed encryption, this + // field contains the encryption configuration for the database. + // For databases that are using Google default or other types of encryption, + // this field is empty. + EncryptionConfig encryption_config = 5 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. For databases that are using customer managed encryption, this + // field contains the encryption information for the database, such as + // all Cloud KMS key versions that are in use. The `encryption_status' field + // inside of each `EncryptionInfo` is not populated. + // + // For databases that are using Google default or other types of encryption, + // this field is empty. + // + // This field is propagated lazily from the backend. There might be a delay + // from when a key version is being used and when it appears in this field. + repeated EncryptionInfo encryption_info = 8 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The period in which Cloud Spanner retains all versions of data + // for the database. This is the same as the value of version_retention_period + // database option set using + // [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. + // Defaults to 1 hour, if not set. + string version_retention_period = 6 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Earliest timestamp at which older versions of the data can be + // read. This value is continuously updated by Cloud Spanner and becomes stale + // the moment it is queried. If you are using this value to recover data, make + // sure to account for the time from the moment when the value is queried to + // the moment when you initiate the recovery. + google.protobuf.Timestamp earliest_version_time = 7 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The read-write region which contains the database's leader + // replicas. + // + // This is the same as the value of default_leader + // database option set using DatabaseAdmin.CreateDatabase or + // DatabaseAdmin.UpdateDatabaseDdl. If not explicitly set, this is empty. + string default_leader = 9 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The dialect of the Cloud Spanner Database. + DatabaseDialect database_dialect = 10 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Whether drop protection is enabled for this database. Defaults to false, + // if not set. For more details, please see how to [prevent accidental + // database + // deletion](https://cloud.google.com/spanner/docs/prevent-database-deletion). + bool enable_drop_protection = 11; + + // Output only. If true, the database is being updated. If false, there are no + // ongoing update operations for the database. + bool reconciling = 12 [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// The request for +// [ListDatabases][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases]. +message ListDatabasesRequest { + // Required. The instance whose databases should be listed. + // Values are of the form `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Number of databases to be returned in the response. If 0 or less, + // defaults to the server's maximum allowed page size. + int32 page_size = 3; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.database.v1.ListDatabasesResponse.next_page_token] + // from a previous + // [ListDatabasesResponse][google.spanner.admin.database.v1.ListDatabasesResponse]. + string page_token = 4; +} + +// The response for +// [ListDatabases][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases]. +message ListDatabasesResponse { + // Databases that matched the request. + repeated Database databases = 1; + + // `next_page_token` can be sent in a subsequent + // [ListDatabases][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases] + // call to fetch more of the matching databases. + string next_page_token = 2; +} + +// The request for +// [CreateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.CreateDatabase]. +message CreateDatabaseRequest { + // Required. The name of the instance that will serve the new database. + // Values are of the form `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Required. A `CREATE DATABASE` statement, which specifies the ID of the + // new database. The database ID must conform to the regular expression + // `[a-z][a-z0-9_\-]*[a-z0-9]` and be between 2 and 30 characters in length. + // If the database ID is a reserved word or if it contains a hyphen, the + // database ID must be enclosed in backticks (`` ` ``). + string create_statement = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. A list of DDL statements to run inside the newly created + // database. Statements can create tables, indexes, etc. These + // statements execute atomically with the creation of the database: + // if there is an error in any statement, the database is not created. + repeated string extra_statements = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The encryption configuration for the database. If this field is + // not specified, Cloud Spanner will encrypt/decrypt all data at rest using + // Google default encryption. + EncryptionConfig encryption_config = 4 + [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The dialect of the Cloud Spanner Database. + DatabaseDialect database_dialect = 5 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Proto descriptors used by CREATE/ALTER PROTO BUNDLE statements in + // 'extra_statements' above. + // Contains a protobuf-serialized + // [google.protobuf.FileDescriptorSet](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/descriptor.proto). + // To generate it, [install](https://grpc.io/docs/protoc-installation/) and + // run `protoc` with --include_imports and --descriptor_set_out. For example, + // to generate for moon/shot/app.proto, run + // ``` + // $protoc --proto_path=/app_path --proto_path=/lib_path \ + // --include_imports \ + // --descriptor_set_out=descriptors.data \ + // moon/shot/app.proto + // ``` + // For more details, see protobuffer [self + // description](https://developers.google.com/protocol-buffers/docs/techniques#self-description). + bytes proto_descriptors = 6 [(google.api.field_behavior) = OPTIONAL]; +} + +// Metadata type for the operation returned by +// [CreateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.CreateDatabase]. +message CreateDatabaseMetadata { + // The database being created. + string database = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; +} + +// The request for +// [GetDatabase][google.spanner.admin.database.v1.DatabaseAdmin.GetDatabase]. +message GetDatabaseRequest { + // Required. The name of the requested database. Values are of the form + // `projects//instances//databases/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; +} + +// The request for +// [UpdateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase]. +message UpdateDatabaseRequest { + // Required. The database to update. + // The `name` field of the database is of the form + // `projects//instances//databases/`. + Database database = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The list of fields to update. Currently, only + // `enable_drop_protection` field can be updated. + google.protobuf.FieldMask update_mask = 2 + [(google.api.field_behavior) = REQUIRED]; +} + +// Metadata type for the operation returned by +// [UpdateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase]. +message UpdateDatabaseMetadata { + // The request for + // [UpdateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase]. + UpdateDatabaseRequest request = 1; + + // The progress of the + // [UpdateDatabase][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase] + // operation. + OperationProgress progress = 2; + + // The time at which this operation was cancelled. If set, this operation is + // in the process of undoing itself (which is best-effort). + google.protobuf.Timestamp cancel_time = 3; +} + +// Enqueues the given DDL statements to be applied, in order but not +// necessarily all at once, to the database schema at some point (or +// points) in the future. The server checks that the statements +// are executable (syntactically valid, name tables that exist, etc.) +// before enqueueing them, but they may still fail upon +// later execution (e.g., if a statement from another batch of +// statements is applied first and it conflicts in some way, or if +// there is some data-related problem like a `NULL` value in a column to +// which `NOT NULL` would be added). If a statement fails, all +// subsequent statements in the batch are automatically cancelled. +// +// Each batch of statements is assigned a name which can be used with +// the [Operations][google.longrunning.Operations] API to monitor +// progress. See the +// [operation_id][google.spanner.admin.database.v1.UpdateDatabaseDdlRequest.operation_id] +// field for more details. +message UpdateDatabaseDdlRequest { + // Required. The database to update. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Required. DDL statements to be applied to the database. + repeated string statements = 2 [(google.api.field_behavior) = REQUIRED]; + + // If empty, the new update request is assigned an + // automatically-generated operation ID. Otherwise, `operation_id` + // is used to construct the name of the resulting + // [Operation][google.longrunning.Operation]. + // + // Specifying an explicit operation ID simplifies determining + // whether the statements were executed in the event that the + // [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl] + // call is replayed, or the return value is otherwise lost: the + // [database][google.spanner.admin.database.v1.UpdateDatabaseDdlRequest.database] + // and `operation_id` fields can be combined to form the + // [name][google.longrunning.Operation.name] of the resulting + // [longrunning.Operation][google.longrunning.Operation]: + // `/operations/`. + // + // `operation_id` should be unique within the database, and must be + // a valid identifier: `[a-z][a-z0-9_]*`. Note that + // automatically-generated operation IDs always begin with an + // underscore. If the named operation already exists, + // [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl] + // returns `ALREADY_EXISTS`. + string operation_id = 3; + + // Optional. Proto descriptors used by CREATE/ALTER PROTO BUNDLE statements. + // Contains a protobuf-serialized + // [google.protobuf.FileDescriptorSet](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/descriptor.proto). + // To generate it, [install](https://grpc.io/docs/protoc-installation/) and + // run `protoc` with --include_imports and --descriptor_set_out. For example, + // to generate for moon/shot/app.proto, run + // ``` + // $protoc --proto_path=/app_path --proto_path=/lib_path \ + // --include_imports \ + // --descriptor_set_out=descriptors.data \ + // moon/shot/app.proto + // ``` + // For more details, see protobuffer [self + // description](https://developers.google.com/protocol-buffers/docs/techniques#self-description). + bytes proto_descriptors = 4 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. This field is exposed to be used by the Spanner Migration Tool. + // For more details, see + // [SMT](https://github.com/GoogleCloudPlatform/spanner-migration-tool). + bool throughput_mode = 5 [(google.api.field_behavior) = OPTIONAL]; +} + +// Action information extracted from a DDL statement. This proto is used to +// display the brief info of the DDL statement for the operation +// [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. +message DdlStatementActionInfo { + // The action for the DDL statement, e.g. CREATE, ALTER, DROP, GRANT, etc. + // This field is a non-empty string. + string action = 1; + + // The entity type for the DDL statement, e.g. TABLE, INDEX, VIEW, etc. + // This field can be empty string for some DDL statement, + // e.g. for statement "ANALYZE", `entity_type` = "". + string entity_type = 2; + + // The entity name(s) being operated on the DDL statement. + // E.g. + // 1. For statement "CREATE TABLE t1(...)", `entity_names` = ["t1"]. + // 2. For statement "GRANT ROLE r1, r2 ...", `entity_names` = ["r1", "r2"]. + // 3. For statement "ANALYZE", `entity_names` = []. + repeated string entity_names = 3; +} + +// Metadata type for the operation returned by +// [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl]. +message UpdateDatabaseDdlMetadata { + // The database being modified. + string database = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; + + // For an update this list contains all the statements. For an + // individual statement, this list contains only that statement. + repeated string statements = 2; + + // Reports the commit timestamps of all statements that have + // succeeded so far, where `commit_timestamps[i]` is the commit + // timestamp for the statement `statements[i]`. + repeated google.protobuf.Timestamp commit_timestamps = 3; + + // Output only. When true, indicates that the operation is throttled e.g. + // due to resource constraints. When resources become available the operation + // will resume and this field will be false again. + bool throttled = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The progress of the + // [UpdateDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabaseDdl] + // operations. All DDL statements will have continuously updating progress, + // and `progress[i]` is the operation progress for `statements[i]`. Also, + // `progress[i]` will have start time and end time populated with commit + // timestamp of operation, as well as a progress of 100% once the operation + // has completed. + repeated OperationProgress progress = 5; + + // The brief action info for the DDL statements. + // `actions[i]` is the brief info for `statements[i]`. + repeated DdlStatementActionInfo actions = 6; +} + +// The request for +// [DropDatabase][google.spanner.admin.database.v1.DatabaseAdmin.DropDatabase]. +message DropDatabaseRequest { + // Required. The database to be dropped. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; +} + +// The request for +// [GetDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDdl]. +message GetDatabaseDdlRequest { + // Required. The database whose schema we wish to get. + // Values are of the form + // `projects//instances//databases/` + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; +} + +// The response for +// [GetDatabaseDdl][google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDdl]. +message GetDatabaseDdlResponse { + // A list of formatted DDL statements defining the schema of the database + // specified in the request. + repeated string statements = 1; + + // Proto descriptors stored in the database. + // Contains a protobuf-serialized + // [google.protobuf.FileDescriptorSet](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/descriptor.proto). + // For more details, see protobuffer [self + // description](https://developers.google.com/protocol-buffers/docs/techniques#self-description). + bytes proto_descriptors = 2; +} + +// The request for +// [ListDatabaseOperations][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabaseOperations]. +message ListDatabaseOperationsRequest { + // Required. The instance of the database operations. + // Values are of the form `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // An expression that filters the list of returned operations. + // + // A filter expression consists of a field name, a + // comparison operator, and a value for filtering. + // The value must be a string, a number, or a boolean. The comparison operator + // must be one of: `<`, `>`, `<=`, `>=`, `!=`, `=`, or `:`. + // Colon `:` is the contains operator. Filter rules are not case sensitive. + // + // The following fields in the [Operation][google.longrunning.Operation] + // are eligible for filtering: + // + // * `name` - The name of the long-running operation + // * `done` - False if the operation is in progress, else true. + // * `metadata.@type` - the type of metadata. For example, the type string + // for + // [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata] + // is + // `type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata`. + // * `metadata.` - any field in metadata.value. + // `metadata.@type` must be specified first, if filtering on metadata + // fields. + // * `error` - Error associated with the long-running operation. + // * `response.@type` - the type of response. + // * `response.` - any field in response.value. + // + // You can combine multiple expressions by enclosing each expression in + // parentheses. By default, expressions are combined with AND logic. However, + // you can specify AND, OR, and NOT logic explicitly. + // + // Here are a few examples: + // + // * `done:true` - The operation is complete. + // * `(metadata.@type=type.googleapis.com/google.spanner.admin.database.v1.RestoreDatabaseMetadata) AND` \ + // `(metadata.source_type:BACKUP) AND` \ + // `(metadata.backup_info.backup:backup_howl) AND` \ + // `(metadata.name:restored_howl) AND` \ + // `(metadata.progress.start_time < \"2018-03-28T14:50:00Z\") AND` \ + // `(error:*)` - Return operations where: + // * The operation's metadata type is + // [RestoreDatabaseMetadata][google.spanner.admin.database.v1.RestoreDatabaseMetadata]. + // * The database is restored from a backup. + // * The backup name contains "backup_howl". + // * The restored database's name contains "restored_howl". + // * The operation started before 2018-03-28T14:50:00Z. + // * The operation resulted in an error. + string filter = 2; + + // Number of operations to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 3; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.database.v1.ListDatabaseOperationsResponse.next_page_token] + // from a previous + // [ListDatabaseOperationsResponse][google.spanner.admin.database.v1.ListDatabaseOperationsResponse] + // to the same `parent` and with the same `filter`. + string page_token = 4; +} + +// The response for +// [ListDatabaseOperations][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabaseOperations]. +message ListDatabaseOperationsResponse { + // The list of matching database [long-running + // operations][google.longrunning.Operation]. Each operation's name will be + // prefixed by the database's name. The operation's + // [metadata][google.longrunning.Operation.metadata] field type + // `metadata.type_url` describes the type of the metadata. + repeated google.longrunning.Operation operations = 1; + + // `next_page_token` can be sent in a subsequent + // [ListDatabaseOperations][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabaseOperations] + // call to fetch more of the matching metadata. + string next_page_token = 2; +} + +// The request for +// [RestoreDatabase][google.spanner.admin.database.v1.DatabaseAdmin.RestoreDatabase]. +message RestoreDatabaseRequest { + // Required. The name of the instance in which to create the + // restored database. This instance must be in the same project and + // have the same instance configuration as the instance containing + // the source backup. Values are of the form + // `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Required. The id of the database to create and restore to. This + // database must not already exist. The `database_id` appended to + // `parent` forms the full database name of the form + // `projects//instances//databases/`. + string database_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The source from which to restore. + oneof source { + // Name of the backup from which to restore. Values are of the form + // `projects//instances//backups/`. + string backup = 3 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Backup" + }]; + } + + // Optional. An encryption configuration describing the encryption type and + // key resources in Cloud KMS used to encrypt/decrypt the database to restore + // to. If this field is not specified, the restored database will use the same + // encryption configuration as the backup by default, namely + // [encryption_type][google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.encryption_type] + // = `USE_CONFIG_DEFAULT_OR_BACKUP_ENCRYPTION`. + RestoreDatabaseEncryptionConfig encryption_config = 4 + [(google.api.field_behavior) = OPTIONAL]; +} + +// Encryption configuration for the restored database. +message RestoreDatabaseEncryptionConfig { + // Encryption types for the database to be restored. + enum EncryptionType { + // Unspecified. Do not use. + ENCRYPTION_TYPE_UNSPECIFIED = 0; + + // This is the default option when + // [encryption_config][google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig] + // is not specified. + USE_CONFIG_DEFAULT_OR_BACKUP_ENCRYPTION = 1; + + // Use Google default encryption. + GOOGLE_DEFAULT_ENCRYPTION = 2; + + // Use customer managed encryption. If specified, `kms_key_name` must + // must contain a valid Cloud KMS key. + CUSTOMER_MANAGED_ENCRYPTION = 3; + } + + // Required. The encryption type of the restored database. + EncryptionType encryption_type = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The Cloud KMS key that will be used to encrypt/decrypt the + // restored database. This field should be set only when + // [encryption_type][google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.encryption_type] + // is `CUSTOMER_MANAGED_ENCRYPTION`. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + string kms_key_name = 2 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + } + ]; + + // Optional. Specifies the KMS configuration for the one or more keys used to + // encrypt the database. Values are of the form + // `projects//locations//keyRings//cryptoKeys/`. + // + // The keys referenced by kms_key_names must fully cover all + // regions of the database instance configuration. Some examples: + // * For single region database instance configs, specify a single regional + // location KMS key. + // * For multi-regional database instance configs of type GOOGLE_MANAGED, + // either specify a multi-regional location KMS key or multiple regional + // location KMS keys that cover all regions in the instance config. + // * For a database instance config of type USER_MANAGED, please specify only + // regional location KMS keys to cover each region in the instance config. + // Multi-regional location KMS keys are not supported for USER_MANAGED + // instance configs. + repeated string kms_key_names = 3 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "cloudkms.googleapis.com/CryptoKey" + } + ]; +} + +// Metadata type for the long-running operation returned by +// [RestoreDatabase][google.spanner.admin.database.v1.DatabaseAdmin.RestoreDatabase]. +message RestoreDatabaseMetadata { + // Name of the database being created and restored to. + string name = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; + + // The type of the restore source. + RestoreSourceType source_type = 2; + + // Information about the source used to restore the database, as specified by + // `source` in + // [RestoreDatabaseRequest][google.spanner.admin.database.v1.RestoreDatabaseRequest]. + oneof source_info { + // Information about the backup used to restore the database. + BackupInfo backup_info = 3; + } + + // The progress of the + // [RestoreDatabase][google.spanner.admin.database.v1.DatabaseAdmin.RestoreDatabase] + // operation. + OperationProgress progress = 4; + + // The time at which cancellation of this operation was received. + // [Operations.CancelOperation][google.longrunning.Operations.CancelOperation] + // starts asynchronous cancellation on a long-running operation. The server + // makes a best effort to cancel the operation, but success is not guaranteed. + // Clients can use + // [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + // other methods to check whether the cancellation succeeded or whether the + // operation completed despite cancellation. On successful cancellation, + // the operation is not deleted; instead, it becomes an operation with + // an [Operation.error][google.longrunning.Operation.error] value with a + // [google.rpc.Status.code][google.rpc.Status.code] of 1, corresponding to + // `Code.CANCELLED`. + google.protobuf.Timestamp cancel_time = 5; + + // If exists, the name of the long-running operation that will be used to + // track the post-restore optimization process to optimize the performance of + // the restored database, and remove the dependency on the restore source. + // The name is of the form + // `projects//instances//databases//operations/` + // where the is the name of database being created and restored to. + // The metadata type of the long-running operation is + // [OptimizeRestoredDatabaseMetadata][google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata]. + // This long-running operation will be automatically created by the system + // after the RestoreDatabase long-running operation completes successfully. + // This operation will not be created if the restore was not successful. + string optimize_database_operation_name = 6; +} + +// Metadata type for the long-running operation used to track the progress +// of optimizations performed on a newly restored database. This long-running +// operation is automatically created by the system after the successful +// completion of a database restore, and cannot be cancelled. +message OptimizeRestoredDatabaseMetadata { + // Name of the restored database being optimized. + string name = 1 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + }]; + + // The progress of the post-restore optimizations. + OperationProgress progress = 2; +} + +// Indicates the type of the restore source. +enum RestoreSourceType { + // No restore associated. + TYPE_UNSPECIFIED = 0; + + // A backup was used as the source of the restore. + BACKUP = 1; +} + +// A Cloud Spanner database role. +message DatabaseRole { + option (google.api.resource) = { + type: "spanner.googleapis.com/DatabaseRole" + pattern: "projects/{project}/instances/{instance}/databases/{database}/databaseRoles/{role}" + }; + + // Required. The name of the database role. Values are of the form + // `projects//instances//databases//databaseRoles/` + // where `` is as specified in the `CREATE ROLE` DDL statement. + string name = 1 [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [ListDatabaseRoles][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabaseRoles]. +message ListDatabaseRolesRequest { + // Required. The database whose roles should be listed. + // Values are of the form + // `projects//instances//databases/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Number of database roles to be returned in the response. If 0 or less, + // defaults to the server's maximum allowed page size. + int32 page_size = 2; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.database.v1.ListDatabaseRolesResponse.next_page_token] + // from a previous + // [ListDatabaseRolesResponse][google.spanner.admin.database.v1.ListDatabaseRolesResponse]. + string page_token = 3; +} + +// The response for +// [ListDatabaseRoles][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabaseRoles]. +message ListDatabaseRolesResponse { + // Database roles that matched the request. + repeated DatabaseRole database_roles = 1; + + // `next_page_token` can be sent in a subsequent + // [ListDatabaseRoles][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabaseRoles] + // call to fetch more of the matching roles. + string next_page_token = 2; +} + +// The request for +// [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints]. +message AddSplitPointsRequest { + // Required. The database on whose tables/indexes split points are to be + // added. Values are of the form + // `projects//instances//databases/`. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Required. The split points to add. + repeated SplitPoints split_points = 2 + [(google.api.field_behavior) = REQUIRED]; + + // Optional. A user-supplied tag associated with the split points. + // For example, "intital_data_load", "special_event_1". + // Defaults to "CloudAddSplitPointsAPI" if not specified. + // The length of the tag must not exceed 50 characters,else will be trimmed. + // Only valid UTF8 characters are allowed. + string initiator = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for +// [AddSplitPoints][google.spanner.admin.database.v1.DatabaseAdmin.AddSplitPoints]. +message AddSplitPointsResponse {} + +// The split points of a table/index. +message SplitPoints { + // A split key. + message Key { + // Required. The column values making up the split key. + google.protobuf.ListValue key_parts = 1 + [(google.api.field_behavior) = REQUIRED]; + } + + // The table to split. + string table = 1; + + // The index to split. + // If specified, the `table` field must refer to the index's base table. + string index = 2; + + // Required. The list of split keys, i.e., the split boundaries. + repeated Key keys = 3 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The expiration timestamp of the split points. + // A timestamp in the past means immediate expiration. + // The maximum value can be 30 days in the future. + // Defaults to 10 days in the future if not specified. + google.protobuf.Timestamp expire_time = 5 + [(google.api.field_behavior) = OPTIONAL]; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_gapic.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_gapic.yaml new file mode 100644 index 00000000..7ea1f7e8 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/database/v1/spanner_gapic.yaml @@ -0,0 +1,32 @@ +type: com.google.api.codegen.ConfigProto +config_schema_version: 2.0.0 +language_settings: + java: + package_name: com.google.cloud.spanner.admin.database.v1 +interfaces: +- name: google.spanner.admin.database.v1.DatabaseAdmin + methods: + - name: CreateDatabase + long_running: + initial_poll_delay_millis: 20000 + poll_delay_multiplier: 1.5 + max_poll_delay_millis: 45000 + total_poll_timeout_millis: 86400000 + - name: UpdateDatabaseDdl + long_running: + initial_poll_delay_millis: 20000 + poll_delay_multiplier: 1.5 + max_poll_delay_millis: 45000 + total_poll_timeout_millis: 86400000 + - name: CreateBackup + long_running: + initial_poll_delay_millis: 20000 + poll_delay_multiplier: 1.5 + max_poll_delay_millis: 45000 + total_poll_timeout_millis: 172800000 + - name: RestoreDatabase + long_running: + initial_poll_delay_millis: 20000 + poll_delay_multiplier: 1.5 + max_poll_delay_millis: 45000 + total_poll_timeout_millis: 86400000 diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/BUILD.bazel new file mode 100644 index 00000000..a87c57fe --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/BUILD.bazel @@ -0,0 +1 @@ +exports_files(glob(["*.yaml"])) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/BUILD.bazel new file mode 100644 index 00000000..e0fb900e --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/BUILD.bazel @@ -0,0 +1,336 @@ +# This file was automatically generated by BuildFileGenerator + +load( + "@com_google_googleapis_imports//:imports.bzl", + "cc_grpc_library", + "cc_proto_library", + "csharp_gapic_assembly_pkg", + "csharp_gapic_library", + "csharp_grpc_library", + "csharp_proto_library", + "go_gapic_assembly_pkg", + "go_gapic_library", + "go_proto_library", + "java_gapic_assembly_gradle_pkg", + "java_gapic_library", + "java_gapic_test", + "java_grpc_library", + "java_proto_library", + "nodejs_gapic_assembly_pkg", + "nodejs_gapic_library", + "php_gapic_assembly_pkg", + "php_gapic_library", + "php_proto_library", + "proto_library_with_info", + "py_gapic_assembly_pkg", + "py_gapic_library", + "py_test", + "ruby_cloud_gapic_library", + "ruby_gapic_assembly_pkg", + "ruby_grpc_library", + "ruby_proto_library", +) + +############################################################################## +# Common +############################################################################## +load("@rules_proto//proto:defs.bzl", "proto_library") + +# This is an API workspace, having public visibility by default makes perfect sense. +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "instance_proto", + srcs = [ + "common.proto", + "spanner_instance_admin.proto", + ], + deps = [ + "//google/api:annotations_proto", + "//google/api:client_proto", + "//google/api:field_behavior_proto", + "//google/api:resource_proto", + "//google/iam/v1:iam_policy_proto", + "//google/iam/v1:policy_proto", + "//google/longrunning:operations_proto", + "@com_google_protobuf//:empty_proto", + "@com_google_protobuf//:field_mask_proto", + "@com_google_protobuf//:timestamp_proto", + ], +) + +proto_library_with_info( + name = "instance_proto_with_info", + deps = [ + ":instance_proto", + "//google/cloud:common_resources_proto", + ], +) + +java_proto_library( + name = "instance_java_proto", + deps = [":instance_proto"], +) + +java_grpc_library( + name = "instance_java_grpc", + srcs = [":instance_proto"], + deps = [":instance_java_proto"], +) + +java_gapic_library( + name = "instance_java_gapic", + srcs = [":instance_proto_with_info"], + gapic_yaml = "spanner_gapic.yaml", + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + test_deps = [ + ":instance_java_grpc", + "//google/iam/v1:iam_java_grpc", + ], + transport = "grpc+rest", + deps = [ + ":instance_java_proto", + "//google/api:api_java_proto", + "//google/iam/v1:iam_java_proto", + ], +) + +java_gapic_test( + name = "instance_java_gapic_test_suite", + test_classes = [ + "com.google.cloud.spanner.admin.instance.v1.InstanceAdminClientHttpJsonTest", + "com.google.cloud.spanner.admin.instance.v1.InstanceAdminClientTest", + ], + runtime_deps = [":instance_java_gapic_test"], +) + +# Open Source Packages +java_gapic_assembly_gradle_pkg( + name = "google-cloud-admin-instance-v1-java", + include_samples = True, + transport = "grpc+rest", + deps = [ + ":instance_java_gapic", + ":instance_java_grpc", + ":instance_java_proto", + ":instance_proto", + ], +) + +go_proto_library( + name = "instance_go_proto", + compilers = ["@io_bazel_rules_go//proto:go_grpc"], + importpath = "cloud.google.com/go/spanner/admin/instance/apiv1/instancepb", + protos = [":instance_proto"], + deps = [ + "//google/api:annotations_go_proto", + "//google/iam/v1:iam_go_proto", + "//google/longrunning:longrunning_go_proto", + ], +) + +go_gapic_library( + name = "instance_go_gapic", + srcs = [":instance_proto_with_info"], + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + importpath = "cloud.google.com/go/spanner/admin/instance/apiv1;instance", + release_level = "ga", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":instance_go_proto", + "//google/iam/v1:iam_go_proto", + "//google/longrunning:longrunning_go_proto", + "@com_google_cloud_go_longrunning//:go_default_library", + "@com_google_cloud_go_longrunning//autogen:go_default_library", + ], +) + +# Open Source Packages +go_gapic_assembly_pkg( + name = "gapi-cloud-admin-instance-v1-go", + deps = [ + ":instance_go_gapic", + ":instance_go_gapic_srcjar-snippets.srcjar", + ":instance_go_gapic_srcjar-test.srcjar", + ":instance_go_proto", + ], +) + +py_gapic_library( + name = "instance_py_gapic", + srcs = [":instance_proto"], + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + opt_args = [ + "python-gapic-namespace=google.cloud", + "python-gapic-name=spanner_admin_instance", + ], + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + "//google/iam/v1:iam_policy_py_proto", + ], +) + +py_test( + name = "instance_py_gapic_test", + srcs = [ + "instance_py_gapic_pytest.py", + "instance_py_gapic_test.py", + ], + legacy_create_init = False, + deps = [":instance_py_gapic"], +) + +py_gapic_assembly_pkg( + name = "admin-instance-v1-py", + deps = [ + ":instance_py_gapic", + ], +) + +php_proto_library( + name = "instance_php_proto", + deps = [":instance_proto"], +) + +php_gapic_library( + name = "instance_php_gapic", + srcs = [":instance_proto_with_info"], + gapic_yaml = "spanner_gapic.yaml", + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + migration_mode = "MIGRATING", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [":instance_php_proto"], +) + +# Open Source Packages +php_gapic_assembly_pkg( + name = "google-cloud-admin-instance-v1-php", + deps = [ + ":instance_php_gapic", + ":instance_php_proto", + ], +) + +nodejs_gapic_library( + name = "instance_nodejs_gapic", + package_name = "@google-cloud/spanner", + src = ":instance_proto_with_info", + extra_protoc_parameters = [ + "metadata", + "template=typescript_gapic", + ], + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + handwritten_layer = True, + package = "google.spanner.admin.instance.v1", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [], +) + +nodejs_gapic_assembly_pkg( + name = "admin-instance-v1-nodejs", + deps = [ + ":instance_nodejs_gapic", + ":instance_proto", + ], +) + +ruby_proto_library( + name = "instance_ruby_proto", + deps = [":instance_proto"], +) + +ruby_grpc_library( + name = "instance_ruby_grpc", + srcs = [":instance_proto"], + deps = [":instance_ruby_proto"], +) + +ruby_cloud_gapic_library( + name = "instance_ruby_gapic", + srcs = [":instance_proto_with_info"], + extra_protoc_parameters = [ + "ruby-cloud-gem-name=google-cloud-spanner-admin-instance-v1", + "ruby-cloud-env-prefix=SPANNER", + "ruby-cloud-product-url=https://cloud.google.com/spanner", + "ruby-cloud-api-id=spanner.googleapis.com", + "ruby-cloud-api-shortname=spanner", + "ruby-cloud-wrapper-gem-override=google-cloud-spanner", + ], + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + rest_numeric_enums = True, + ruby_cloud_description = "Cloud Spanner is a managed, mission-critical, globally consistent and scalable relational database service.", + ruby_cloud_title = "Cloud Spanner Instance Admin V1", + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":instance_ruby_grpc", + ":instance_ruby_proto", + ], +) + +# Open Source Packages +ruby_gapic_assembly_pkg( + name = "google-cloud-admin-instance-v1-ruby", + deps = [ + ":instance_ruby_gapic", + ":instance_ruby_grpc", + ":instance_ruby_proto", + ], +) + +csharp_proto_library( + name = "instance_csharp_proto", + deps = [":instance_proto"], +) + +csharp_grpc_library( + name = "instance_csharp_grpc", + srcs = [":instance_proto"], + deps = [":instance_csharp_proto"], +) + +csharp_gapic_library( + name = "instance_csharp_gapic", + srcs = [":instance_proto_with_info"], + common_resources_config = "@gax_dotnet//:Google.Api.Gax/ResourceNames/CommonResourcesConfig.json", + grpc_service_config = "spanner_admin_instance_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":instance_csharp_grpc", + ":instance_csharp_proto", + ], +) + +# Open Source Packages +csharp_gapic_assembly_pkg( + name = "google-cloud-admin-instance-v1-csharp", + deps = [ + ":instance_csharp_gapic", + ":instance_csharp_grpc", + ":instance_csharp_proto", + ], +) + +cc_proto_library( + name = "instance_cc_proto", + deps = [":instance_proto"], +) + +cc_grpc_library( + name = "instance_cc_grpc", + srcs = [":instance_proto"], + grpc_only = True, + deps = [":instance_cc_proto"], +) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/common.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/common.proto new file mode 100644 index 00000000..0b5282c7 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/common.proto @@ -0,0 +1,64 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.admin.instance.v1; + +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Admin.Instance.V1"; +option go_package = "cloud.google.com/go/spanner/admin/instance/apiv1/instancepb;instancepb"; +option java_multiple_files = true; +option java_outer_classname = "CommonProto"; +option java_package = "com.google.spanner.admin.instance.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Instance\\V1"; +option ruby_package = "Google::Cloud::Spanner::Admin::Instance::V1"; + +// Encapsulates progress related information for a Cloud Spanner long +// running instance operations. +message OperationProgress { + // Percent completion of the operation. + // Values are between 0 and 100 inclusive. + int32 progress_percent = 1; + + // Time the request was received. + google.protobuf.Timestamp start_time = 2; + + // If set, the time at which this operation failed or was completed + // successfully. + google.protobuf.Timestamp end_time = 3; +} + +// Indicates the expected fulfillment period of an operation. +enum FulfillmentPeriod { + // Not specified. + FULFILLMENT_PERIOD_UNSPECIFIED = 0; + + // Normal fulfillment period. The operation is expected to complete within + // minutes. + FULFILLMENT_PERIOD_NORMAL = 1; + + // Extended fulfillment period. It can take up to an hour for the operation + // to complete. + FULFILLMENT_PERIOD_EXTENDED = 2; +} + +// ReplicaSelection identifies replicas with common properties. +message ReplicaSelection { + // Required. Name of the location of the replicas (e.g., "us-central1"). + string location = 1 [(google.api.field_behavior) = REQUIRED]; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner.yaml new file mode 100644 index 00000000..04941812 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner.yaml @@ -0,0 +1,73 @@ +type: google.api.Service +config_version: 3 +name: spanner.googleapis.com +title: Cloud Spanner API + +apis: +- name: google.longrunning.Operations +- name: google.spanner.admin.instance.v1.InstanceAdmin + +types: +- name: google.spanner.admin.instance.v1.CreateInstanceConfigMetadata +- name: google.spanner.admin.instance.v1.CreateInstanceMetadata +- name: google.spanner.admin.instance.v1.CreateInstancePartitionMetadata +- name: google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata +- name: google.spanner.admin.instance.v1.UpdateInstanceMetadata +- name: google.spanner.admin.instance.v1.UpdateInstancePartitionMetadata + +documentation: + summary: |- + Cloud Spanner is a managed, mission-critical, globally consistent and + scalable relational database service. + +http: + rules: + - selector: google.longrunning.Operations.CancelOperation + post: '/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel' + additional_bindings: + - post: '/v1/{name=projects/*/instances/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instances/*/backups/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instanceConfigs/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}:cancel' + - selector: google.longrunning.Operations.DeleteOperation + delete: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - delete: '/v1/{name=projects/*/instances/*/operations/*}' + - delete: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - delete: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}' + - delete: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - delete: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}' + - selector: google.longrunning.Operations.GetOperation + get: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations/*}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - get: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - get: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}' + - selector: google.longrunning.Operations.ListOperations + get: '/v1/{name=projects/*/instances/*/databases/*/operations}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations}' + - get: '/v1/{name=projects/*/instances/*/instancePartitions/*/operations}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations}' + - get: '/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations}' + +authentication: + rules: + - selector: 'google.longrunning.Operations.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.admin + - selector: 'google.spanner.admin.instance.v1.InstanceAdmin.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.admin + +publishing: + new_issue_uri: https://issuetracker.google.com/issues/new?component=190851&template=0 + documentation_uri: https://cloud.google.com/spanner/ diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_admin_instance_grpc_service_config.json b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_admin_instance_grpc_service_config.json new file mode 100755 index 00000000..f1241560 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_admin_instance_grpc_service_config.json @@ -0,0 +1,82 @@ +{ + "methodConfig": [ + { + "name": [ + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "ListInstanceConfigs" + }, + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "GetInstanceConfig" + }, + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "ListInstances" + }, + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "GetInstance" + }, + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "DeleteInstance" + } + ], + "timeout": "3600s", + "retryPolicy": { + "initialBackoff": "1s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "DEADLINE_EXCEEDED" + ] + } + }, + { + "name": [ + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "GetIamPolicy" + } + ], + "timeout": "30s", + "retryPolicy": { + "initialBackoff": "1s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "DEADLINE_EXCEEDED" + ] + } + }, + { + "name": [ + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "CreateInstance" + }, + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "UpdateInstance" + } + ], + "timeout": "3600s" + }, + { + "name": [ + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "SetIamPolicy" + }, + { + "service": "google.spanner.admin.instance.v1.InstanceAdmin", + "method": "TestIamPermissions" + } + ], + "timeout": "30s" + } + ] +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_gapic.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_gapic.yaml new file mode 100644 index 00000000..89d4488b --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_gapic.yaml @@ -0,0 +1,20 @@ +type: com.google.api.codegen.ConfigProto +config_schema_version: 2.0.0 +language_settings: + java: + package_name: com.google.cloud.spanner.admin.instance.v1 +interfaces: +- name: google.spanner.admin.instance.v1.InstanceAdmin + methods: + - name: CreateInstance + long_running: + initial_poll_delay_millis: 20000 + poll_delay_multiplier: 1.5 + max_poll_delay_millis: 45000 + total_poll_timeout_millis: 86400000 + - name: UpdateInstance + long_running: + initial_poll_delay_millis: 20000 + poll_delay_multiplier: 1.5 + max_poll_delay_millis: 45000 + total_poll_timeout_millis: 86400000 diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_instance_admin.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_instance_admin.proto new file mode 100644 index 00000000..d3e60257 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/admin/instance/v1/spanner_instance_admin.proto @@ -0,0 +1,2110 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.admin.instance.v1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/iam/v1/iam_policy.proto"; +import "google/iam/v1/policy.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/spanner/admin/instance/v1/common.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.Admin.Instance.V1"; +option go_package = "cloud.google.com/go/spanner/admin/instance/apiv1/instancepb;instancepb"; +option java_multiple_files = true; +option java_outer_classname = "SpannerInstanceAdminProto"; +option java_package = "com.google.spanner.admin.instance.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Instance\\V1"; +option ruby_package = "Google::Cloud::Spanner::Admin::Instance::V1"; + +// Cloud Spanner Instance Admin API +// +// The Cloud Spanner Instance Admin API can be used to create, delete, +// modify and list instances. Instances are dedicated Cloud Spanner serving +// and storage resources to be used by Cloud Spanner databases. +// +// Each instance has a "configuration", which dictates where the +// serving resources for the Cloud Spanner instance are located (e.g., +// US-central, Europe). Configurations are created by Google based on +// resource availability. +// +// Cloud Spanner billing is based on the instances that exist and their +// sizes. After an instance exists, there are no additional +// per-database or per-operation charges for use of the instance +// (though there may be additional network bandwidth charges). +// Instances offer isolation: problems with databases in one instance +// will not affect other instances. However, within an instance +// databases can affect each other. For example, if one database in an +// instance receives a lot of requests and consumes most of the +// instance resources, fewer resources are available for other +// databases in that instance, and their performance may suffer. +service InstanceAdmin { + option (google.api.default_host) = "spanner.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/spanner.admin"; + + // Lists the supported instance configurations for a given project. + // + // Returns both Google-managed configurations and user-managed + // configurations. + rpc ListInstanceConfigs(ListInstanceConfigsRequest) + returns (ListInstanceConfigsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*}/instanceConfigs" + }; + option (google.api.method_signature) = "parent"; + } + + // Gets information about a particular instance configuration. + rpc GetInstanceConfig(GetInstanceConfigRequest) returns (InstanceConfig) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instanceConfigs/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Creates an instance configuration and begins preparing it to be used. The + // returned long-running operation + // can be used to track the progress of preparing the new + // instance configuration. The instance configuration name is assigned by the + // caller. If the named instance configuration already exists, + // `CreateInstanceConfig` returns `ALREADY_EXISTS`. + // + // Immediately after the request returns: + // + // * The instance configuration is readable via the API, with all requested + // attributes. The instance configuration's + // [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] + // field is set to true. Its state is `CREATING`. + // + // While the operation is pending: + // + // * Cancelling the operation renders the instance configuration immediately + // unreadable via the API. + // * Except for deleting the creating resource, all other attempts to modify + // the instance configuration are rejected. + // + // Upon completion of the returned operation: + // + // * Instances can be created using the instance configuration. + // * The instance configuration's + // [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] + // field becomes false. Its state becomes `READY`. + // + // The returned long-running operation will + // have a name of the format + // `/operations/` and can be used to track + // creation of the instance configuration. The + // metadata field type is + // [CreateInstanceConfigMetadata][google.spanner.admin.instance.v1.CreateInstanceConfigMetadata]. + // The response field type is + // [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig], if + // successful. + // + // Authorization requires `spanner.instanceConfigs.create` permission on + // the resource + // [parent][google.spanner.admin.instance.v1.CreateInstanceConfigRequest.parent]. + rpc CreateInstanceConfig(CreateInstanceConfigRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*}/instanceConfigs" + body: "*" + }; + option (google.api.method_signature) = + "parent,instance_config,instance_config_id"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.InstanceConfig" + metadata_type: "google.spanner.admin.instance.v1.CreateInstanceConfigMetadata" + }; + } + + // Updates an instance configuration. The returned + // long-running operation can be used to track + // the progress of updating the instance. If the named instance configuration + // does not exist, returns `NOT_FOUND`. + // + // Only user-managed configurations can be updated. + // + // Immediately after the request returns: + // + // * The instance configuration's + // [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] + // field is set to true. + // + // While the operation is pending: + // + // * Cancelling the operation sets its metadata's + // [cancel_time][google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata.cancel_time]. + // The operation is guaranteed to succeed at undoing all changes, after + // which point it terminates with a `CANCELLED` status. + // * All other attempts to modify the instance configuration are rejected. + // * Reading the instance configuration via the API continues to give the + // pre-request values. + // + // Upon completion of the returned operation: + // + // * Creating instances using the instance configuration uses the new + // values. + // * The new values of the instance configuration are readable via the API. + // * The instance configuration's + // [reconciling][google.spanner.admin.instance.v1.InstanceConfig.reconciling] + // field becomes false. + // + // The returned long-running operation will + // have a name of the format + // `/operations/` and can be used to track + // the instance configuration modification. The + // metadata field type is + // [UpdateInstanceConfigMetadata][google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata]. + // The response field type is + // [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig], if + // successful. + // + // Authorization requires `spanner.instanceConfigs.update` permission on + // the resource [name][google.spanner.admin.instance.v1.InstanceConfig.name]. + rpc UpdateInstanceConfig(UpdateInstanceConfigRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + patch: "/v1/{instance_config.name=projects/*/instanceConfigs/*}" + body: "*" + }; + option (google.api.method_signature) = "instance_config,update_mask"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.InstanceConfig" + metadata_type: "google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata" + }; + } + + // Deletes the instance configuration. Deletion is only allowed when no + // instances are using the configuration. If any instances are using + // the configuration, returns `FAILED_PRECONDITION`. + // + // Only user-managed configurations can be deleted. + // + // Authorization requires `spanner.instanceConfigs.delete` permission on + // the resource [name][google.spanner.admin.instance.v1.InstanceConfig.name]. + rpc DeleteInstanceConfig(DeleteInstanceConfigRequest) + returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/instanceConfigs/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Lists the user-managed instance configuration long-running + // operations in the given project. An instance + // configuration operation has a name of the form + // `projects//instanceConfigs//operations/`. + // The long-running operation + // metadata field type + // `metadata.type_url` describes the type of the metadata. Operations returned + // include those that have completed/failed/canceled within the last 7 days, + // and pending operations. Operations returned are ordered by + // `operation.metadata.value.start_time` in descending order starting + // from the most recently started operation. + rpc ListInstanceConfigOperations(ListInstanceConfigOperationsRequest) + returns (ListInstanceConfigOperationsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*}/instanceConfigOperations" + }; + option (google.api.method_signature) = "parent"; + } + + // Lists all instances in the given project. + rpc ListInstances(ListInstancesRequest) returns (ListInstancesResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*}/instances" + }; + option (google.api.method_signature) = "parent"; + } + + // Lists all instance partitions for the given instance. + rpc ListInstancePartitions(ListInstancePartitionsRequest) + returns (ListInstancePartitionsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*}/instancePartitions" + }; + option (google.api.method_signature) = "parent"; + } + + // Gets information about a particular instance. + rpc GetInstance(GetInstanceRequest) returns (Instance) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instances/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Creates an instance and begins preparing it to begin serving. The + // returned long-running operation + // can be used to track the progress of preparing the new + // instance. The instance name is assigned by the caller. If the + // named instance already exists, `CreateInstance` returns + // `ALREADY_EXISTS`. + // + // Immediately upon completion of this request: + // + // * The instance is readable via the API, with all requested attributes + // but no allocated resources. Its state is `CREATING`. + // + // Until completion of the returned operation: + // + // * Cancelling the operation renders the instance immediately unreadable + // via the API. + // * The instance can be deleted. + // * All other attempts to modify the instance are rejected. + // + // Upon completion of the returned operation: + // + // * Billing for all successfully-allocated resources begins (some types + // may have lower than the requested levels). + // * Databases can be created in the instance. + // * The instance's allocated resource levels are readable via the API. + // * The instance's state becomes `READY`. + // + // The returned long-running operation will + // have a name of the format `/operations/` and + // can be used to track creation of the instance. The + // metadata field type is + // [CreateInstanceMetadata][google.spanner.admin.instance.v1.CreateInstanceMetadata]. + // The response field type is + // [Instance][google.spanner.admin.instance.v1.Instance], if successful. + rpc CreateInstance(CreateInstanceRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*}/instances" + body: "*" + }; + option (google.api.method_signature) = "parent,instance_id,instance"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.Instance" + metadata_type: "google.spanner.admin.instance.v1.CreateInstanceMetadata" + }; + } + + // Updates an instance, and begins allocating or releasing resources + // as requested. The returned long-running operation can be used to track the + // progress of updating the instance. If the named instance does not + // exist, returns `NOT_FOUND`. + // + // Immediately upon completion of this request: + // + // * For resource types for which a decrease in the instance's allocation + // has been requested, billing is based on the newly-requested level. + // + // Until completion of the returned operation: + // + // * Cancelling the operation sets its metadata's + // [cancel_time][google.spanner.admin.instance.v1.UpdateInstanceMetadata.cancel_time], + // and begins restoring resources to their pre-request values. The + // operation is guaranteed to succeed at undoing all resource changes, + // after which point it terminates with a `CANCELLED` status. + // * All other attempts to modify the instance are rejected. + // * Reading the instance via the API continues to give the pre-request + // resource levels. + // + // Upon completion of the returned operation: + // + // * Billing begins for all successfully-allocated resources (some types + // may have lower than the requested levels). + // * All newly-reserved resources are available for serving the instance's + // tables. + // * The instance's new resource levels are readable via the API. + // + // The returned long-running operation will + // have a name of the format `/operations/` and + // can be used to track the instance modification. The + // metadata field type is + // [UpdateInstanceMetadata][google.spanner.admin.instance.v1.UpdateInstanceMetadata]. + // The response field type is + // [Instance][google.spanner.admin.instance.v1.Instance], if successful. + // + // Authorization requires `spanner.instances.update` permission on + // the resource [name][google.spanner.admin.instance.v1.Instance.name]. + rpc UpdateInstance(UpdateInstanceRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + patch: "/v1/{instance.name=projects/*/instances/*}" + body: "*" + }; + option (google.api.method_signature) = "instance,field_mask"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.Instance" + metadata_type: "google.spanner.admin.instance.v1.UpdateInstanceMetadata" + }; + } + + // Deletes an instance. + // + // Immediately upon completion of the request: + // + // * Billing ceases for all of the instance's reserved resources. + // + // Soon afterward: + // + // * The instance and *all of its databases* immediately and + // irrevocably disappear from the API. All data in the databases + // is permanently deleted. + rpc DeleteInstance(DeleteInstanceRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/instances/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Sets the access control policy on an instance resource. Replaces any + // existing policy. + // + // Authorization requires `spanner.instances.setIamPolicy` on + // [resource][google.iam.v1.SetIamPolicyRequest.resource]. + rpc SetIamPolicy(google.iam.v1.SetIamPolicyRequest) + returns (google.iam.v1.Policy) { + option (google.api.http) = { + post: "/v1/{resource=projects/*/instances/*}:setIamPolicy" + body: "*" + }; + option (google.api.method_signature) = "resource,policy"; + } + + // Gets the access control policy for an instance resource. Returns an empty + // policy if an instance exists but does not have a policy set. + // + // Authorization requires `spanner.instances.getIamPolicy` on + // [resource][google.iam.v1.GetIamPolicyRequest.resource]. + rpc GetIamPolicy(google.iam.v1.GetIamPolicyRequest) + returns (google.iam.v1.Policy) { + option (google.api.http) = { + post: "/v1/{resource=projects/*/instances/*}:getIamPolicy" + body: "*" + }; + option (google.api.method_signature) = "resource"; + } + + // Returns permissions that the caller has on the specified instance resource. + // + // Attempting this RPC on a non-existent Cloud Spanner instance resource will + // result in a NOT_FOUND error if the user has `spanner.instances.list` + // permission on the containing Google Cloud Project. Otherwise returns an + // empty set of permissions. + rpc TestIamPermissions(google.iam.v1.TestIamPermissionsRequest) + returns (google.iam.v1.TestIamPermissionsResponse) { + option (google.api.http) = { + post: "/v1/{resource=projects/*/instances/*}:testIamPermissions" + body: "*" + }; + option (google.api.method_signature) = "resource,permissions"; + } + + // Gets information about a particular instance partition. + rpc GetInstancePartition(GetInstancePartitionRequest) + returns (InstancePartition) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instances/*/instancePartitions/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Creates an instance partition and begins preparing it to be used. The + // returned long-running operation + // can be used to track the progress of preparing the new instance partition. + // The instance partition name is assigned by the caller. If the named + // instance partition already exists, `CreateInstancePartition` returns + // `ALREADY_EXISTS`. + // + // Immediately upon completion of this request: + // + // * The instance partition is readable via the API, with all requested + // attributes but no allocated resources. Its state is `CREATING`. + // + // Until completion of the returned operation: + // + // * Cancelling the operation renders the instance partition immediately + // unreadable via the API. + // * The instance partition can be deleted. + // * All other attempts to modify the instance partition are rejected. + // + // Upon completion of the returned operation: + // + // * Billing for all successfully-allocated resources begins (some types + // may have lower than the requested levels). + // * Databases can start using this instance partition. + // * The instance partition's allocated resource levels are readable via the + // API. + // * The instance partition's state becomes `READY`. + // + // The returned long-running operation will + // have a name of the format + // `/operations/` and can be used to + // track creation of the instance partition. The + // metadata field type is + // [CreateInstancePartitionMetadata][google.spanner.admin.instance.v1.CreateInstancePartitionMetadata]. + // The response field type is + // [InstancePartition][google.spanner.admin.instance.v1.InstancePartition], if + // successful. + rpc CreateInstancePartition(CreateInstancePartitionRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=projects/*/instances/*}/instancePartitions" + body: "*" + }; + option (google.api.method_signature) = + "parent,instance_partition,instance_partition_id"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.InstancePartition" + metadata_type: "google.spanner.admin.instance.v1.CreateInstancePartitionMetadata" + }; + } + + // Deletes an existing instance partition. Requires that the + // instance partition is not used by any database or backup and is not the + // default instance partition of an instance. + // + // Authorization requires `spanner.instancePartitions.delete` permission on + // the resource + // [name][google.spanner.admin.instance.v1.InstancePartition.name]. + rpc DeleteInstancePartition(DeleteInstancePartitionRequest) + returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/instances/*/instancePartitions/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Updates an instance partition, and begins allocating or releasing resources + // as requested. The returned long-running operation can be used to track the + // progress of updating the instance partition. If the named instance + // partition does not exist, returns `NOT_FOUND`. + // + // Immediately upon completion of this request: + // + // * For resource types for which a decrease in the instance partition's + // allocation has been requested, billing is based on the newly-requested + // level. + // + // Until completion of the returned operation: + // + // * Cancelling the operation sets its metadata's + // [cancel_time][google.spanner.admin.instance.v1.UpdateInstancePartitionMetadata.cancel_time], + // and begins restoring resources to their pre-request values. The + // operation is guaranteed to succeed at undoing all resource changes, + // after which point it terminates with a `CANCELLED` status. + // * All other attempts to modify the instance partition are rejected. + // * Reading the instance partition via the API continues to give the + // pre-request resource levels. + // + // Upon completion of the returned operation: + // + // * Billing begins for all successfully-allocated resources (some types + // may have lower than the requested levels). + // * All newly-reserved resources are available for serving the instance + // partition's tables. + // * The instance partition's new resource levels are readable via the API. + // + // The returned long-running operation will + // have a name of the format + // `/operations/` and can be used to + // track the instance partition modification. The + // metadata field type is + // [UpdateInstancePartitionMetadata][google.spanner.admin.instance.v1.UpdateInstancePartitionMetadata]. + // The response field type is + // [InstancePartition][google.spanner.admin.instance.v1.InstancePartition], if + // successful. + // + // Authorization requires `spanner.instancePartitions.update` permission on + // the resource + // [name][google.spanner.admin.instance.v1.InstancePartition.name]. + rpc UpdateInstancePartition(UpdateInstancePartitionRequest) + returns (google.longrunning.Operation) { + option (google.api.http) = { + patch: "/v1/{instance_partition.name=projects/*/instances/*/instancePartitions/*}" + body: "*" + }; + option (google.api.method_signature) = "instance_partition,field_mask"; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.InstancePartition" + metadata_type: "google.spanner.admin.instance.v1.UpdateInstancePartitionMetadata" + }; + } + + // Lists instance partition long-running operations in the given instance. + // An instance partition operation has a name of the form + // `projects//instances//instancePartitions//operations/`. + // The long-running operation + // metadata field type + // `metadata.type_url` describes the type of the metadata. Operations returned + // include those that have completed/failed/canceled within the last 7 days, + // and pending operations. Operations returned are ordered by + // `operation.metadata.value.start_time` in descending order starting from the + // most recently started operation. + // + // Authorization requires `spanner.instancePartitionOperations.list` + // permission on the resource + // [parent][google.spanner.admin.instance.v1.ListInstancePartitionOperationsRequest.parent]. + rpc ListInstancePartitionOperations(ListInstancePartitionOperationsRequest) + returns (ListInstancePartitionOperationsResponse) { + option (google.api.http) = { + get: "/v1/{parent=projects/*/instances/*}/instancePartitionOperations" + }; + option (google.api.method_signature) = "parent"; + } + + // Moves an instance to the target instance configuration. You can use the + // returned long-running operation to track + // the progress of moving the instance. + // + // `MoveInstance` returns `FAILED_PRECONDITION` if the instance meets any of + // the following criteria: + // + // * Is undergoing a move to a different instance configuration + // * Has backups + // * Has an ongoing update + // * Contains any CMEK-enabled databases + // * Is a free trial instance + // + // While the operation is pending: + // + // * All other attempts to modify the instance, including changes to its + // compute capacity, are rejected. + // * The following database and backup admin operations are rejected: + // + // * `DatabaseAdmin.CreateDatabase` + // * `DatabaseAdmin.UpdateDatabaseDdl` (disabled if default_leader is + // specified in the request.) + // * `DatabaseAdmin.RestoreDatabase` + // * `DatabaseAdmin.CreateBackup` + // * `DatabaseAdmin.CopyBackup` + // + // * Both the source and target instance configurations are subject to + // hourly compute and storage charges. + // * The instance might experience higher read-write latencies and a higher + // transaction abort rate. However, moving an instance doesn't cause any + // downtime. + // + // The returned long-running operation has + // a name of the format + // `/operations/` and can be used to track + // the move instance operation. The + // metadata field type is + // [MoveInstanceMetadata][google.spanner.admin.instance.v1.MoveInstanceMetadata]. + // The response field type is + // [Instance][google.spanner.admin.instance.v1.Instance], + // if successful. + // Cancelling the operation sets its metadata's + // [cancel_time][google.spanner.admin.instance.v1.MoveInstanceMetadata.cancel_time]. + // Cancellation is not immediate because it involves moving any data + // previously moved to the target instance configuration back to the original + // instance configuration. You can use this operation to track the progress of + // the cancellation. Upon successful completion of the cancellation, the + // operation terminates with `CANCELLED` status. + // + // If not cancelled, upon completion of the returned operation: + // + // * The instance successfully moves to the target instance + // configuration. + // * You are billed for compute and storage in target instance + // configuration. + // + // Authorization requires the `spanner.instances.update` permission on + // the resource [instance][google.spanner.admin.instance.v1.Instance]. + // + // For more details, see + // [Move an instance](https://cloud.google.com/spanner/docs/move-instance). + rpc MoveInstance(MoveInstanceRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{name=projects/*/instances/*}:move" + body: "*" + }; + option (google.longrunning.operation_info) = { + response_type: "google.spanner.admin.instance.v1.MoveInstanceResponse" + metadata_type: "google.spanner.admin.instance.v1.MoveInstanceMetadata" + }; + } +} + +message ReplicaInfo { + // Indicates the type of replica. See the [replica types + // documentation](https://cloud.google.com/spanner/docs/replication#replica_types) + // for more details. + enum ReplicaType { + // Not specified. + TYPE_UNSPECIFIED = 0; + + // Read-write replicas support both reads and writes. These replicas: + // + // * Maintain a full copy of your data. + // * Serve reads. + // * Can vote whether to commit a write. + // * Participate in leadership election. + // * Are eligible to become a leader. + READ_WRITE = 1; + + // Read-only replicas only support reads (not writes). Read-only replicas: + // + // * Maintain a full copy of your data. + // * Serve reads. + // * Do not participate in voting to commit writes. + // * Are not eligible to become a leader. + READ_ONLY = 2; + + // Witness replicas don't support reads but do participate in voting to + // commit writes. Witness replicas: + // + // * Do not maintain a full copy of data. + // * Do not serve reads. + // * Vote whether to commit writes. + // * Participate in leader election but are not eligible to become leader. + WITNESS = 3; + } + + // The location of the serving resources, e.g., "us-central1". + string location = 1; + + // The type of replica. + ReplicaType type = 2; + + // If true, this location is designated as the default leader location where + // leader replicas are placed. See the [region types + // documentation](https://cloud.google.com/spanner/docs/instances#region_types) + // for more details. + bool default_leader_location = 3; +} + +// A possible configuration for a Cloud Spanner instance. Configurations +// define the geographic placement of nodes and their replication. +message InstanceConfig { + option (google.api.resource) = { + type: "spanner.googleapis.com/InstanceConfig" + pattern: "projects/{project}/instanceConfigs/{instance_config}" + plural: "instanceConfigs" + singular: "instanceConfig" + }; + + // The type of this configuration. + enum Type { + // Unspecified. + TYPE_UNSPECIFIED = 0; + + // Google-managed configuration. + GOOGLE_MANAGED = 1; + + // User-managed configuration. + USER_MANAGED = 2; + } + + // Indicates the current state of the instance configuration. + enum State { + // Not specified. + STATE_UNSPECIFIED = 0; + + // The instance configuration is still being created. + CREATING = 1; + + // The instance configuration is fully created and ready to be used to + // create instances. + READY = 2; + } + + // Describes the availability for free instances to be created in an instance + // configuration. + enum FreeInstanceAvailability { + // Not specified. + FREE_INSTANCE_AVAILABILITY_UNSPECIFIED = 0; + + // Indicates that free instances are available to be created in this + // instance configuration. + AVAILABLE = 1; + + // Indicates that free instances are not supported in this instance + // configuration. + UNSUPPORTED = 2; + + // Indicates that free instances are currently not available to be created + // in this instance configuration. + DISABLED = 3; + + // Indicates that additional free instances cannot be created in this + // instance configuration because the project has reached its limit of free + // instances. + QUOTA_EXCEEDED = 4; + } + + // Indicates the quorum type of this instance configuration. + enum QuorumType { + // Quorum type not specified. + QUORUM_TYPE_UNSPECIFIED = 0; + + // An instance configuration tagged with `REGION` quorum type forms a write + // quorum in a single region. + REGION = 1; + + // An instance configuration tagged with the `DUAL_REGION` quorum type forms + // a write quorum with exactly two read-write regions in a multi-region + // configuration. + // + // This instance configuration requires failover in the event of + // regional failures. + DUAL_REGION = 2; + + // An instance configuration tagged with the `MULTI_REGION` quorum type + // forms a write quorum from replicas that are spread across more than one + // region in a multi-region configuration. + MULTI_REGION = 3; + } + + // A unique identifier for the instance configuration. Values + // are of the form + // `projects//instanceConfigs/[a-z][-a-z0-9]*`. + // + // User instance configuration must start with `custom-`. + string name = 1; + + // The name of this instance configuration as it appears in UIs. + string display_name = 2; + + // Output only. Whether this instance configuration is a Google-managed or + // user-managed configuration. + Type config_type = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The geographic placement of nodes in this instance configuration and their + // replication properties. + // + // To create user-managed configurations, input + // `replicas` must include all replicas in `replicas` of the `base_config` + // and include one or more replicas in the `optional_replicas` of the + // `base_config`. + repeated ReplicaInfo replicas = 3; + + // Output only. The available optional replicas to choose from for + // user-managed configurations. Populated for Google-managed configurations. + repeated ReplicaInfo optional_replicas = 6 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Base configuration name, e.g. projects//instanceConfigs/nam3, + // based on which this configuration is created. Only set for user-managed + // configurations. `base_config` must refer to a configuration of type + // `GOOGLE_MANAGED` in the same project as this configuration. + string base_config = 7 [(google.api.resource_reference) = { + type: "spanner.googleapis.com/InstanceConfig" + }]; + + // Cloud Labels are a flexible and lightweight mechanism for organizing cloud + // resources into groups that reflect a customer's organizational needs and + // deployment strategies. Cloud Labels can be used to filter collections of + // resources. They can be used to control how resource metrics are aggregated. + // And they can be used as arguments to policy management rules (e.g. route, + // firewall, load balancing, etc.). + // + // * Label keys must be between 1 and 63 characters long and must conform to + // the following regular expression: `[a-z][a-z0-9_-]{0,62}`. + // * Label values must be between 0 and 63 characters long and must conform + // to the regular expression `[a-z0-9_-]{0,63}`. + // * No more than 64 labels can be associated with a given resource. + // + // See https://goo.gl/xmQnxf for more information on and examples of labels. + // + // If you plan to use labels in your own code, please note that additional + // characters may be allowed in the future. Therefore, you are advised to use + // an internal label representation, such as JSON, which doesn't rely upon + // specific characters being disallowed. For example, representing labels + // as the string: name + "_" + value would prove problematic if we were to + // allow "_" in a future release. + map labels = 8; + + // etag is used for optimistic concurrency control as a way + // to help prevent simultaneous updates of a instance configuration from + // overwriting each other. It is strongly suggested that systems make use of + // the etag in the read-modify-write cycle to perform instance configuration + // updates in order to avoid race conditions: An etag is returned in the + // response which contains instance configurations, and systems are expected + // to put that etag in the request to update instance configuration to ensure + // that their change is applied to the same version of the instance + // configuration. If no etag is provided in the call to update the instance + // configuration, then the existing instance configuration is overwritten + // blindly. + string etag = 9; + + // Allowed values of the "default_leader" schema option for databases in + // instances that use this instance configuration. + repeated string leader_options = 4; + + // Output only. If true, the instance configuration is being created or + // updated. If false, there are no ongoing operations for the instance + // configuration. + bool reconciling = 10 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The current instance configuration state. Applicable only for + // `USER_MANAGED` configurations. + State state = 11 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Describes whether free instances are available to be created + // in this instance configuration. + FreeInstanceAvailability free_instance_availability = 12 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The `QuorumType` of the instance configuration. + QuorumType quorum_type = 18 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The storage limit in bytes per processing unit. + int64 storage_limit_per_processing_unit = 19 + [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// ReplicaComputeCapacity describes the amount of server resources that are +// allocated to each replica identified by the replica selection. +message ReplicaComputeCapacity { + // Required. Identifies replicas by specified properties. + // All replicas in the selection have the same amount of compute capacity. + ReplicaSelection replica_selection = 1 + [(google.api.field_behavior) = REQUIRED]; + + // Compute capacity allocated to each replica identified by the specified + // selection. + // The unit is selected based on the unit used to specify the instance size + // for non-autoscaling instances, or the unit used in autoscaling limit for + // autoscaling instances. + oneof compute_capacity { + // The number of nodes allocated to each replica. + // + // This may be zero in API responses for instances that are not yet in + // state `READY`. + int32 node_count = 2; + + // The number of processing units allocated to each replica. + // + // This may be zero in API responses for instances that are not yet in + // state `READY`. + int32 processing_units = 3; + } +} + +// Autoscaling configuration for an instance. +message AutoscalingConfig { + // The autoscaling limits for the instance. Users can define the minimum and + // maximum compute capacity allocated to the instance, and the autoscaler will + // only scale within that range. Users can either use nodes or processing + // units to specify the limits, but should use the same unit to set both the + // min_limit and max_limit. + message AutoscalingLimits { + // The minimum compute capacity for the instance. + oneof min_limit { + // Minimum number of nodes allocated to the instance. If set, this number + // should be greater than or equal to 1. + int32 min_nodes = 1; + + // Minimum number of processing units allocated to the instance. If set, + // this number should be multiples of 1000. + int32 min_processing_units = 2; + } + + // The maximum compute capacity for the instance. The maximum compute + // capacity should be less than or equal to 10X the minimum compute + // capacity. + oneof max_limit { + // Maximum number of nodes allocated to the instance. If set, this number + // should be greater than or equal to min_nodes. + int32 max_nodes = 3; + + // Maximum number of processing units allocated to the instance. If set, + // this number should be multiples of 1000 and be greater than or equal to + // min_processing_units. + int32 max_processing_units = 4; + } + } + + // The autoscaling targets for an instance. + message AutoscalingTargets { + // Required. The target high priority cpu utilization percentage that the + // autoscaler should be trying to achieve for the instance. This number is + // on a scale from 0 (no utilization) to 100 (full utilization). The valid + // range is [10, 90] inclusive. + int32 high_priority_cpu_utilization_percent = 1 + [(google.api.field_behavior) = REQUIRED]; + + // Required. The target storage utilization percentage that the autoscaler + // should be trying to achieve for the instance. This number is on a scale + // from 0 (no utilization) to 100 (full utilization). The valid range is + // [10, 99] inclusive. + int32 storage_utilization_percent = 2 + [(google.api.field_behavior) = REQUIRED]; + } + + // AsymmetricAutoscalingOption specifies the scaling of replicas identified by + // the given selection. + message AsymmetricAutoscalingOption { + // Overrides the top-level autoscaling configuration for the replicas + // identified by `replica_selection`. All fields in this message are + // optional. Any unspecified fields will use the corresponding values from + // the top-level autoscaling configuration. + message AutoscalingConfigOverrides { + // Optional. If specified, overrides the min/max limit in the top-level + // autoscaling configuration for the selected replicas. + AutoscalingLimits autoscaling_limits = 1 + [(google.api.field_behavior) = OPTIONAL]; + + // Optional. If specified, overrides the autoscaling target + // high_priority_cpu_utilization_percent in the top-level autoscaling + // configuration for the selected replicas. + int32 autoscaling_target_high_priority_cpu_utilization_percent = 2 + [(google.api.field_behavior) = OPTIONAL]; + } + + // Required. Selects the replicas to which this AsymmetricAutoscalingOption + // applies. Only read-only replicas are supported. + ReplicaSelection replica_selection = 1 + [(google.api.field_behavior) = REQUIRED]; + + // Optional. Overrides applied to the top-level autoscaling configuration + // for the selected replicas. + AutoscalingConfigOverrides overrides = 2 + [(google.api.field_behavior) = OPTIONAL]; + } + + // Required. Autoscaling limits for an instance. + AutoscalingLimits autoscaling_limits = 1 + [(google.api.field_behavior) = REQUIRED]; + + // Required. The autoscaling targets for an instance. + AutoscalingTargets autoscaling_targets = 2 + [(google.api.field_behavior) = REQUIRED]; + + // Optional. Optional asymmetric autoscaling options. + // Replicas matching the replica selection criteria will be autoscaled + // independently from other replicas. The autoscaler will scale the replicas + // based on the utilization of replicas identified by the replica selection. + // Replica selections should not overlap with each other. + // + // Other replicas (those do not match any replica selection) will be + // autoscaled together and will have the same compute capacity allocated to + // them. + repeated AsymmetricAutoscalingOption asymmetric_autoscaling_options = 3 + [(google.api.field_behavior) = OPTIONAL]; +} + +// An isolated set of Cloud Spanner resources on which databases can be hosted. +message Instance { + option (google.api.resource) = { + type: "spanner.googleapis.com/Instance" + pattern: "projects/{project}/instances/{instance}" + plural: "instances" + singular: "instance" + }; + + // Indicates the current state of the instance. + enum State { + // Not specified. + STATE_UNSPECIFIED = 0; + + // The instance is still being created. Resources may not be + // available yet, and operations such as database creation may not + // work. + CREATING = 1; + + // The instance is fully created and ready to do work such as + // creating databases. + READY = 2; + } + + // The type of this instance. The type can be used to distinguish product + // variants, that can affect aspects like: usage restrictions, quotas and + // billing. Currently this is used to distinguish FREE_INSTANCE vs PROVISIONED + // instances. + enum InstanceType { + // Not specified. + INSTANCE_TYPE_UNSPECIFIED = 0; + + // Provisioned instances have dedicated resources, standard usage limits and + // support. + PROVISIONED = 1; + + // Free instances provide no guarantee for dedicated resources, + // [node_count, processing_units] should be 0. They come + // with stricter usage limits and limited support. + FREE_INSTANCE = 2; + } + + // The edition selected for this instance. Different editions provide + // different capabilities at different price points. + enum Edition { + // Edition not specified. + EDITION_UNSPECIFIED = 0; + + // Standard edition. + STANDARD = 1; + + // Enterprise edition. + ENTERPRISE = 2; + + // Enterprise Plus edition. + ENTERPRISE_PLUS = 3; + } + + // Indicates the + // [default backup + // schedule](https://cloud.google.com/spanner/docs/backup#default-backup-schedules) + // behavior for new databases within the instance. + enum DefaultBackupScheduleType { + // Not specified. + DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED = 0; + + // A default backup schedule isn't created automatically when a new database + // is created in the instance. + NONE = 1; + + // A default backup schedule is created automatically when a new database + // is created in the instance. The default backup schedule creates a full + // backup every 24 hours. These full backups are retained for 7 days. + // You can edit or delete the default backup schedule once it's created. + AUTOMATIC = 2; + } + + // Required. A unique identifier for the instance, which cannot be changed + // after the instance is created. Values are of the form + // `projects//instances/[a-z][-a-z0-9]*[a-z0-9]`. The final + // segment of the name must be between 2 and 64 characters in length. + string name = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The name of the instance's configuration. Values are of the form + // `projects//instanceConfigs/`. See + // also [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] and + // [ListInstanceConfigs][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs]. + string config = 2 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstanceConfig" + } + ]; + + // Required. The descriptive name for this instance as it appears in UIs. + // Must be unique per project and between 4 and 30 characters in length. + string display_name = 3 [(google.api.field_behavior) = REQUIRED]; + + // The number of nodes allocated to this instance. At most, one of either + // `node_count` or `processing_units` should be present in the message. + // + // Users can set the `node_count` field to specify the target number of nodes + // allocated to the instance. + // + // If autoscaling is enabled, `node_count` is treated as an `OUTPUT_ONLY` + // field and reflects the current number of nodes allocated to the instance. + // + // This might be zero in API responses for instances that are not yet in the + // `READY` state. + // + // + // For more information, see + // [Compute capacity, nodes, and processing + // units](https://cloud.google.com/spanner/docs/compute-capacity). + int32 node_count = 5; + + // The number of processing units allocated to this instance. At most, one of + // either `processing_units` or `node_count` should be present in the message. + // + // Users can set the `processing_units` field to specify the target number of + // processing units allocated to the instance. + // + // If autoscaling is enabled, `processing_units` is treated as an + // `OUTPUT_ONLY` field and reflects the current number of processing units + // allocated to the instance. + // + // This might be zero in API responses for instances that are not yet in the + // `READY` state. + // + // + // For more information, see + // [Compute capacity, nodes and processing + // units](https://cloud.google.com/spanner/docs/compute-capacity). + int32 processing_units = 9; + + // Output only. Lists the compute capacity per ReplicaSelection. A replica + // selection identifies a set of replicas with common properties. Replicas + // identified by a ReplicaSelection are scaled with the same compute capacity. + repeated ReplicaComputeCapacity replica_compute_capacity = 19 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Optional. The autoscaling configuration. Autoscaling is enabled if this + // field is set. When autoscaling is enabled, node_count and processing_units + // are treated as OUTPUT_ONLY fields and reflect the current compute capacity + // allocated to the instance. + AutoscalingConfig autoscaling_config = 17 + [(google.api.field_behavior) = OPTIONAL]; + + // Output only. The current instance state. For + // [CreateInstance][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance], + // the state must be either omitted or set to `CREATING`. For + // [UpdateInstance][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance], + // the state must be either omitted or set to `READY`. + State state = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Cloud Labels are a flexible and lightweight mechanism for organizing cloud + // resources into groups that reflect a customer's organizational needs and + // deployment strategies. Cloud Labels can be used to filter collections of + // resources. They can be used to control how resource metrics are aggregated. + // And they can be used as arguments to policy management rules (e.g. route, + // firewall, load balancing, etc.). + // + // * Label keys must be between 1 and 63 characters long and must conform to + // the following regular expression: `[a-z][a-z0-9_-]{0,62}`. + // * Label values must be between 0 and 63 characters long and must conform + // to the regular expression `[a-z0-9_-]{0,63}`. + // * No more than 64 labels can be associated with a given resource. + // + // See https://goo.gl/xmQnxf for more information on and examples of labels. + // + // If you plan to use labels in your own code, please note that additional + // characters may be allowed in the future. And so you are advised to use an + // internal label representation, such as JSON, which doesn't rely upon + // specific characters being disallowed. For example, representing labels + // as the string: name + "_" + value would prove problematic if we were to + // allow "_" in a future release. + map labels = 7; + + // The `InstanceType` of the current instance. + InstanceType instance_type = 10; + + // Deprecated. This field is not populated. + repeated string endpoint_uris = 8; + + // Output only. The time at which the instance was created. + google.protobuf.Timestamp create_time = 11 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time at which the instance was most recently updated. + google.protobuf.Timestamp update_time = 12 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Free instance metadata. Only populated for free instances. + FreeInstanceMetadata free_instance_metadata = 13; + + // Optional. The `Edition` of the current instance. + Edition edition = 20 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Controls the default backup schedule behavior for new databases + // within the instance. By default, a backup schedule is created automatically + // when a new database is created in a new instance. + // + // Note that the `AUTOMATIC` value isn't permitted for free instances, + // as backups and backup schedules aren't supported for free instances. + // + // In the `GetInstance` or `ListInstances` response, if the value of + // `default_backup_schedule_type` isn't set, or set to `NONE`, Spanner doesn't + // create a default backup schedule for new databases in the instance. + DefaultBackupScheduleType default_backup_schedule_type = 23 + [(google.api.field_behavior) = OPTIONAL]; +} + +// The request for +// [ListInstanceConfigs][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs]. +message ListInstanceConfigsRequest { + // Required. The name of the project for which a list of supported instance + // configurations is requested. Values are of the form + // `projects/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "cloudresourcemanager.googleapis.com/Project" + } + ]; + + // Number of instance configurations to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 2; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.instance.v1.ListInstanceConfigsResponse.next_page_token] + // from a previous + // [ListInstanceConfigsResponse][google.spanner.admin.instance.v1.ListInstanceConfigsResponse]. + string page_token = 3; +} + +// The response for +// [ListInstanceConfigs][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs]. +message ListInstanceConfigsResponse { + // The list of requested instance configurations. + repeated InstanceConfig instance_configs = 1; + + // `next_page_token` can be sent in a subsequent + // [ListInstanceConfigs][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs] + // call to fetch more of the matching instance configurations. + string next_page_token = 2; +} + +// The request for +// [GetInstanceConfigRequest][google.spanner.admin.instance.v1.InstanceAdmin.GetInstanceConfig]. +message GetInstanceConfigRequest { + // Required. The name of the requested instance configuration. Values are of + // the form `projects//instanceConfigs/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstanceConfig" + } + ]; +} + +// The request for +// [CreateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstanceConfig]. +message CreateInstanceConfigRequest { + // Required. The name of the project in which to create the instance + // configuration. Values are of the form `projects/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "cloudresourcemanager.googleapis.com/Project" + } + ]; + + // Required. The ID of the instance configuration to create. Valid identifiers + // are of the form `custom-[-a-z0-9]*[a-z0-9]` and must be between 2 and 64 + // characters in length. The `custom-` prefix is required to avoid name + // conflicts with Google-managed configurations. + string instance_config_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The `InstanceConfig` proto of the configuration to create. + // `instance_config.name` must be + // `/instanceConfigs/`. + // `instance_config.base_config` must be a Google-managed configuration name, + // e.g. /instanceConfigs/us-east1, /instanceConfigs/nam3. + InstanceConfig instance_config = 3 [(google.api.field_behavior) = REQUIRED]; + + // An option to validate, but not actually execute, a request, + // and provide the same response. + bool validate_only = 4; +} + +// The request for +// [UpdateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstanceConfig]. +message UpdateInstanceConfigRequest { + // Required. The user instance configuration to update, which must always + // include the instance configuration name. Otherwise, only fields mentioned + // in + // [update_mask][google.spanner.admin.instance.v1.UpdateInstanceConfigRequest.update_mask] + // need be included. To prevent conflicts of concurrent updates, + // [etag][google.spanner.admin.instance.v1.InstanceConfig.reconciling] can + // be used. + InstanceConfig instance_config = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. A mask specifying which fields in + // [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] should be + // updated. The field mask must always be specified; this prevents any future + // fields in [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] + // from being erased accidentally by clients that do not know about them. Only + // display_name and labels can be updated. + google.protobuf.FieldMask update_mask = 2 + [(google.api.field_behavior) = REQUIRED]; + + // An option to validate, but not actually execute, a request, + // and provide the same response. + bool validate_only = 3; +} + +// The request for +// [DeleteInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.DeleteInstanceConfig]. +message DeleteInstanceConfigRequest { + // Required. The name of the instance configuration to be deleted. + // Values are of the form + // `projects//instanceConfigs/` + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstanceConfig" + } + ]; + + // Used for optimistic concurrency control as a way to help prevent + // simultaneous deletes of an instance configuration from overwriting each + // other. If not empty, the API + // only deletes the instance configuration when the etag provided matches the + // current status of the requested instance configuration. Otherwise, deletes + // the instance configuration without checking the current status of the + // requested instance configuration. + string etag = 2; + + // An option to validate, but not actually execute, a request, + // and provide the same response. + bool validate_only = 3; +} + +// The request for +// [ListInstanceConfigOperations][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigOperations]. +message ListInstanceConfigOperationsRequest { + // Required. The project of the instance configuration operations. + // Values are of the form `projects/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "cloudresourcemanager.googleapis.com/Project" + } + ]; + + // An expression that filters the list of returned operations. + // + // A filter expression consists of a field name, a + // comparison operator, and a value for filtering. + // The value must be a string, a number, or a boolean. The comparison operator + // must be one of: `<`, `>`, `<=`, `>=`, `!=`, `=`, or `:`. + // Colon `:` is the contains operator. Filter rules are not case sensitive. + // + // The following fields in the Operation are eligible for filtering: + // + // * `name` - The name of the long-running operation + // * `done` - False if the operation is in progress, else true. + // * `metadata.@type` - the type of metadata. For example, the type string + // for + // [CreateInstanceConfigMetadata][google.spanner.admin.instance.v1.CreateInstanceConfigMetadata] + // is + // `type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceConfigMetadata`. + // * `metadata.` - any field in metadata.value. + // `metadata.@type` must be specified first, if filtering on metadata + // fields. + // * `error` - Error associated with the long-running operation. + // * `response.@type` - the type of response. + // * `response.` - any field in response.value. + // + // You can combine multiple expressions by enclosing each expression in + // parentheses. By default, expressions are combined with AND logic. However, + // you can specify AND, OR, and NOT logic explicitly. + // + // Here are a few examples: + // + // * `done:true` - The operation is complete. + // * `(metadata.@type=` \ + // `type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceConfigMetadata) + // AND` \ + // `(metadata.instance_config.name:custom-config) AND` \ + // `(metadata.progress.start_time < \"2021-03-28T14:50:00Z\") AND` \ + // `(error:*)` - Return operations where: + // * The operation's metadata type is + // [CreateInstanceConfigMetadata][google.spanner.admin.instance.v1.CreateInstanceConfigMetadata]. + // * The instance configuration name contains "custom-config". + // * The operation started before 2021-03-28T14:50:00Z. + // * The operation resulted in an error. + string filter = 2; + + // Number of operations to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 3; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.instance.v1.ListInstanceConfigOperationsResponse.next_page_token] + // from a previous + // [ListInstanceConfigOperationsResponse][google.spanner.admin.instance.v1.ListInstanceConfigOperationsResponse] + // to the same `parent` and with the same `filter`. + string page_token = 4; +} + +// The response for +// [ListInstanceConfigOperations][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigOperations]. +message ListInstanceConfigOperationsResponse { + // The list of matching instance configuration long-running operations. Each + // operation's name will be + // prefixed by the name of the instance configuration. The operation's + // metadata field type + // `metadata.type_url` describes the type of the metadata. + repeated google.longrunning.Operation operations = 1; + + // `next_page_token` can be sent in a subsequent + // [ListInstanceConfigOperations][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigOperations] + // call to fetch more of the matching metadata. + string next_page_token = 2; +} + +// The request for +// [GetInstance][google.spanner.admin.instance.v1.InstanceAdmin.GetInstance]. +message GetInstanceRequest { + // Required. The name of the requested instance. Values are of the form + // `projects//instances/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // If field_mask is present, specifies the subset of + // [Instance][google.spanner.admin.instance.v1.Instance] fields that should be + // returned. If absent, all + // [Instance][google.spanner.admin.instance.v1.Instance] fields are returned. + google.protobuf.FieldMask field_mask = 2; +} + +// The request for +// [CreateInstance][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance]. +message CreateInstanceRequest { + // Required. The name of the project in which to create the instance. Values + // are of the form `projects/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "cloudresourcemanager.googleapis.com/Project" + } + ]; + + // Required. The ID of the instance to create. Valid identifiers are of the + // form `[a-z][-a-z0-9]*[a-z0-9]` and must be between 2 and 64 characters in + // length. + string instance_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The instance to create. The name may be omitted, but if + // specified must be `/instances/`. + Instance instance = 3 [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [ListInstances][google.spanner.admin.instance.v1.InstanceAdmin.ListInstances]. +message ListInstancesRequest { + // Required. The name of the project for which a list of instances is + // requested. Values are of the form `projects/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "cloudresourcemanager.googleapis.com/Project" + } + ]; + + // Number of instances to be returned in the response. If 0 or less, defaults + // to the server's maximum allowed page size. + int32 page_size = 2; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.instance.v1.ListInstancesResponse.next_page_token] + // from a previous + // [ListInstancesResponse][google.spanner.admin.instance.v1.ListInstancesResponse]. + string page_token = 3; + + // An expression for filtering the results of the request. Filter rules are + // case insensitive. The fields eligible for filtering are: + // + // * `name` + // * `display_name` + // * `labels.key` where key is the name of a label + // + // Some examples of using filters are: + // + // * `name:*` --> The instance has a name. + // * `name:Howl` --> The instance's name contains the string "howl". + // * `name:HOWL` --> Equivalent to above. + // * `NAME:howl` --> Equivalent to above. + // * `labels.env:*` --> The instance has the label "env". + // * `labels.env:dev` --> The instance has the label "env" and the value of + // the label contains the string "dev". + // * `name:howl labels.env:dev` --> The instance's name contains "howl" and + // it has the label "env" with its value + // containing "dev". + string filter = 4; + + // Deadline used while retrieving metadata for instances. + // Instances whose metadata cannot be retrieved within this deadline will be + // added to + // [unreachable][google.spanner.admin.instance.v1.ListInstancesResponse.unreachable] + // in + // [ListInstancesResponse][google.spanner.admin.instance.v1.ListInstancesResponse]. + google.protobuf.Timestamp instance_deadline = 5; +} + +// The response for +// [ListInstances][google.spanner.admin.instance.v1.InstanceAdmin.ListInstances]. +message ListInstancesResponse { + // The list of requested instances. + repeated Instance instances = 1; + + // `next_page_token` can be sent in a subsequent + // [ListInstances][google.spanner.admin.instance.v1.InstanceAdmin.ListInstances] + // call to fetch more of the matching instances. + string next_page_token = 2; + + // The list of unreachable instances. + // It includes the names of instances whose metadata could not be retrieved + // within + // [instance_deadline][google.spanner.admin.instance.v1.ListInstancesRequest.instance_deadline]. + repeated string unreachable = 3; +} + +// The request for +// [UpdateInstance][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance]. +message UpdateInstanceRequest { + // Required. The instance to update, which must always include the instance + // name. Otherwise, only fields mentioned in + // [field_mask][google.spanner.admin.instance.v1.UpdateInstanceRequest.field_mask] + // need be included. + Instance instance = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. A mask specifying which fields in + // [Instance][google.spanner.admin.instance.v1.Instance] should be updated. + // The field mask must always be specified; this prevents any future fields in + // [Instance][google.spanner.admin.instance.v1.Instance] from being erased + // accidentally by clients that do not know about them. + google.protobuf.FieldMask field_mask = 2 + [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [DeleteInstance][google.spanner.admin.instance.v1.InstanceAdmin.DeleteInstance]. +message DeleteInstanceRequest { + // Required. The name of the instance to be deleted. Values are of the form + // `projects//instances/` + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; +} + +// Metadata type for the operation returned by +// [CreateInstance][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance]. +message CreateInstanceMetadata { + // The instance being created. + Instance instance = 1; + + // The time at which the + // [CreateInstance][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance] + // request was received. + google.protobuf.Timestamp start_time = 2; + + // The time at which this operation was cancelled. If set, this operation is + // in the process of undoing itself (which is guaranteed to succeed) and + // cannot be cancelled again. + google.protobuf.Timestamp cancel_time = 3; + + // The time at which this operation failed or was completed successfully. + google.protobuf.Timestamp end_time = 4; + + // The expected fulfillment period of this create operation. + FulfillmentPeriod expected_fulfillment_period = 5; +} + +// Metadata type for the operation returned by +// [UpdateInstance][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance]. +message UpdateInstanceMetadata { + // The desired end state of the update. + Instance instance = 1; + + // The time at which + // [UpdateInstance][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance] + // request was received. + google.protobuf.Timestamp start_time = 2; + + // The time at which this operation was cancelled. If set, this operation is + // in the process of undoing itself (which is guaranteed to succeed) and + // cannot be cancelled again. + google.protobuf.Timestamp cancel_time = 3; + + // The time at which this operation failed or was completed successfully. + google.protobuf.Timestamp end_time = 4; + + // The expected fulfillment period of this update operation. + FulfillmentPeriod expected_fulfillment_period = 5; +} + +// Free instance specific metadata that is kept even after an instance has been +// upgraded for tracking purposes. +message FreeInstanceMetadata { + // Allows users to change behavior when a free instance expires. + enum ExpireBehavior { + // Not specified. + EXPIRE_BEHAVIOR_UNSPECIFIED = 0; + + // When the free instance expires, upgrade the instance to a provisioned + // instance. + FREE_TO_PROVISIONED = 1; + + // When the free instance expires, disable the instance, and delete it + // after the grace period passes if it has not been upgraded. + REMOVE_AFTER_GRACE_PERIOD = 2; + } + + // Output only. Timestamp after which the instance will either be upgraded or + // scheduled for deletion after a grace period. ExpireBehavior is used to + // choose between upgrading or scheduling the free instance for deletion. This + // timestamp is set during the creation of a free instance. + google.protobuf.Timestamp expire_time = 1 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. If present, the timestamp at which the free instance was + // upgraded to a provisioned instance. + google.protobuf.Timestamp upgrade_time = 2 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Specifies the expiration behavior of a free instance. The default of + // ExpireBehavior is `REMOVE_AFTER_GRACE_PERIOD`. This can be modified during + // or after creation, and before expiration. + ExpireBehavior expire_behavior = 3; +} + +// Metadata type for the operation returned by +// [CreateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstanceConfig]. +message CreateInstanceConfigMetadata { + // The target instance configuration end state. + InstanceConfig instance_config = 1; + + // The progress of the + // [CreateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstanceConfig] + // operation. + OperationProgress progress = 2; + + // The time at which this operation was cancelled. + google.protobuf.Timestamp cancel_time = 3; +} + +// Metadata type for the operation returned by +// [UpdateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstanceConfig]. +message UpdateInstanceConfigMetadata { + // The desired instance configuration after updating. + InstanceConfig instance_config = 1; + + // The progress of the + // [UpdateInstanceConfig][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstanceConfig] + // operation. + OperationProgress progress = 2; + + // The time at which this operation was cancelled. + google.protobuf.Timestamp cancel_time = 3; +} + +// An isolated set of Cloud Spanner resources that databases can define +// placements on. +message InstancePartition { + option (google.api.resource) = { + type: "spanner.googleapis.com/InstancePartition" + pattern: "projects/{project}/instances/{instance}/instancePartitions/{instance_partition}" + plural: "instancePartitions" + singular: "instancePartition" + }; + + // Indicates the current state of the instance partition. + enum State { + // Not specified. + STATE_UNSPECIFIED = 0; + + // The instance partition is still being created. Resources may not be + // available yet, and operations such as creating placements using this + // instance partition may not work. + CREATING = 1; + + // The instance partition is fully created and ready to do work such as + // creating placements and using in databases. + READY = 2; + } + + // Required. A unique identifier for the instance partition. Values are of the + // form + // `projects//instances//instancePartitions/[a-z][-a-z0-9]*[a-z0-9]`. + // The final segment of the name must be between 2 and 64 characters in + // length. An instance partition's name cannot be changed after the instance + // partition is created. + string name = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The name of the instance partition's configuration. Values are of + // the form `projects//instanceConfigs/`. See also + // [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] and + // [ListInstanceConfigs][google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs]. + string config = 2 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstanceConfig" + } + ]; + + // Required. The descriptive name for this instance partition as it appears in + // UIs. Must be unique per project and between 4 and 30 characters in length. + string display_name = 3 [(google.api.field_behavior) = REQUIRED]; + + // Compute capacity defines amount of server and storage resources that are + // available to the databases in an instance partition. At most, one of either + // `node_count` or` processing_units` should be present in the message. For + // more information, see + // [Compute capacity, nodes, and processing + // units](https://cloud.google.com/spanner/docs/compute-capacity). + oneof compute_capacity { + // The number of nodes allocated to this instance partition. + // + // Users can set the `node_count` field to specify the target number of + // nodes allocated to the instance partition. + // + // This may be zero in API responses for instance partitions that are not + // yet in state `READY`. + int32 node_count = 5; + + // The number of processing units allocated to this instance partition. + // + // Users can set the `processing_units` field to specify the target number + // of processing units allocated to the instance partition. + // + // This might be zero in API responses for instance partitions that are not + // yet in the `READY` state. + int32 processing_units = 6; + } + + // Output only. The current instance partition state. + State state = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time at which the instance partition was created. + google.protobuf.Timestamp create_time = 8 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time at which the instance partition was most recently + // updated. + google.protobuf.Timestamp update_time = 9 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The names of the databases that reference this + // instance partition. Referencing databases should share the parent instance. + // The existence of any referencing database prevents the instance partition + // from being deleted. + repeated string referencing_databases = 10 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Deprecated: This field is not populated. + // Output only. The names of the backups that reference this instance + // partition. Referencing backups should share the parent instance. The + // existence of any referencing backup prevents the instance partition from + // being deleted. + repeated string referencing_backups = 11 + [deprecated = true, (google.api.field_behavior) = OUTPUT_ONLY]; + + // Used for optimistic concurrency control as a way + // to help prevent simultaneous updates of a instance partition from + // overwriting each other. It is strongly suggested that systems make use of + // the etag in the read-modify-write cycle to perform instance partition + // updates in order to avoid race conditions: An etag is returned in the + // response which contains instance partitions, and systems are expected to + // put that etag in the request to update instance partitions to ensure that + // their change will be applied to the same version of the instance partition. + // If no etag is provided in the call to update instance partition, then the + // existing instance partition is overwritten blindly. + string etag = 12; +} + +// Metadata type for the operation returned by +// [CreateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstancePartition]. +message CreateInstancePartitionMetadata { + // The instance partition being created. + InstancePartition instance_partition = 1; + + // The time at which the + // [CreateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstancePartition] + // request was received. + google.protobuf.Timestamp start_time = 2; + + // The time at which this operation was cancelled. If set, this operation is + // in the process of undoing itself (which is guaranteed to succeed) and + // cannot be cancelled again. + google.protobuf.Timestamp cancel_time = 3; + + // The time at which this operation failed or was completed successfully. + google.protobuf.Timestamp end_time = 4; +} + +// The request for +// [CreateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.CreateInstancePartition]. +message CreateInstancePartitionRequest { + // Required. The name of the instance in which to create the instance + // partition. Values are of the form + // `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Required. The ID of the instance partition to create. Valid identifiers are + // of the form `[a-z][-a-z0-9]*[a-z0-9]` and must be between 2 and 64 + // characters in length. + string instance_partition_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The instance partition to create. The instance_partition.name may + // be omitted, but if specified must be + // `/instancePartitions/`. + InstancePartition instance_partition = 3 + [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [DeleteInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.DeleteInstancePartition]. +message DeleteInstancePartitionRequest { + // Required. The name of the instance partition to be deleted. + // Values are of the form + // `projects/{project}/instances/{instance}/instancePartitions/{instance_partition}` + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstancePartition" + } + ]; + + // Optional. If not empty, the API only deletes the instance partition when + // the etag provided matches the current status of the requested instance + // partition. Otherwise, deletes the instance partition without checking the + // current status of the requested instance partition. + string etag = 2; +} + +// The request for +// [GetInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.GetInstancePartition]. +message GetInstancePartitionRequest { + // Required. The name of the requested instance partition. Values are of + // the form + // `projects/{project}/instances/{instance}/instancePartitions/{instance_partition}`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstancePartition" + } + ]; +} + +// The request for +// [UpdateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstancePartition]. +message UpdateInstancePartitionRequest { + // Required. The instance partition to update, which must always include the + // instance partition name. Otherwise, only fields mentioned in + // [field_mask][google.spanner.admin.instance.v1.UpdateInstancePartitionRequest.field_mask] + // need be included. + InstancePartition instance_partition = 1 + [(google.api.field_behavior) = REQUIRED]; + + // Required. A mask specifying which fields in + // [InstancePartition][google.spanner.admin.instance.v1.InstancePartition] + // should be updated. The field mask must always be specified; this prevents + // any future fields in + // [InstancePartition][google.spanner.admin.instance.v1.InstancePartition] + // from being erased accidentally by clients that do not know about them. + google.protobuf.FieldMask field_mask = 2 + [(google.api.field_behavior) = REQUIRED]; +} + +// Metadata type for the operation returned by +// [UpdateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstancePartition]. +message UpdateInstancePartitionMetadata { + // The desired end state of the update. + InstancePartition instance_partition = 1; + + // The time at which + // [UpdateInstancePartition][google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstancePartition] + // request was received. + google.protobuf.Timestamp start_time = 2; + + // The time at which this operation was cancelled. If set, this operation is + // in the process of undoing itself (which is guaranteed to succeed) and + // cannot be cancelled again. + google.protobuf.Timestamp cancel_time = 3; + + // The time at which this operation failed or was completed successfully. + google.protobuf.Timestamp end_time = 4; +} + +// The request for +// [ListInstancePartitions][google.spanner.admin.instance.v1.InstanceAdmin.ListInstancePartitions]. +message ListInstancePartitionsRequest { + // Required. The instance whose instance partitions should be listed. Values + // are of the form `projects//instances/`. Use `{instance} + // = '-'` to list instance partitions for all Instances in a project, e.g., + // `projects/myproject/instances/-`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Number of instance partitions to be returned in the response. If 0 or less, + // defaults to the server's maximum allowed page size. + int32 page_size = 2; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.instance.v1.ListInstancePartitionsResponse.next_page_token] + // from a previous + // [ListInstancePartitionsResponse][google.spanner.admin.instance.v1.ListInstancePartitionsResponse]. + string page_token = 3; + + // Optional. Deadline used while retrieving metadata for instance partitions. + // Instance partitions whose metadata cannot be retrieved within this deadline + // will be added to + // [unreachable][google.spanner.admin.instance.v1.ListInstancePartitionsResponse.unreachable] + // in + // [ListInstancePartitionsResponse][google.spanner.admin.instance.v1.ListInstancePartitionsResponse]. + google.protobuf.Timestamp instance_partition_deadline = 4 + [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for +// [ListInstancePartitions][google.spanner.admin.instance.v1.InstanceAdmin.ListInstancePartitions]. +message ListInstancePartitionsResponse { + // The list of requested instancePartitions. + repeated InstancePartition instance_partitions = 1; + + // `next_page_token` can be sent in a subsequent + // [ListInstancePartitions][google.spanner.admin.instance.v1.InstanceAdmin.ListInstancePartitions] + // call to fetch more of the matching instance partitions. + string next_page_token = 2; + + // The list of unreachable instances or instance partitions. + // It includes the names of instances or instance partitions whose metadata + // could not be retrieved within + // [instance_partition_deadline][google.spanner.admin.instance.v1.ListInstancePartitionsRequest.instance_partition_deadline]. + repeated string unreachable = 3; +} + +// The request for +// [ListInstancePartitionOperations][google.spanner.admin.instance.v1.InstanceAdmin.ListInstancePartitionOperations]. +message ListInstancePartitionOperationsRequest { + // Required. The parent instance of the instance partition operations. + // Values are of the form `projects//instances/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Optional. An expression that filters the list of returned operations. + // + // A filter expression consists of a field name, a + // comparison operator, and a value for filtering. + // The value must be a string, a number, or a boolean. The comparison operator + // must be one of: `<`, `>`, `<=`, `>=`, `!=`, `=`, or `:`. + // Colon `:` is the contains operator. Filter rules are not case sensitive. + // + // The following fields in the Operation are eligible for filtering: + // + // * `name` - The name of the long-running operation + // * `done` - False if the operation is in progress, else true. + // * `metadata.@type` - the type of metadata. For example, the type string + // for + // [CreateInstancePartitionMetadata][google.spanner.admin.instance.v1.CreateInstancePartitionMetadata] + // is + // `type.googleapis.com/google.spanner.admin.instance.v1.CreateInstancePartitionMetadata`. + // * `metadata.` - any field in metadata.value. + // `metadata.@type` must be specified first, if filtering on metadata + // fields. + // * `error` - Error associated with the long-running operation. + // * `response.@type` - the type of response. + // * `response.` - any field in response.value. + // + // You can combine multiple expressions by enclosing each expression in + // parentheses. By default, expressions are combined with AND logic. However, + // you can specify AND, OR, and NOT logic explicitly. + // + // Here are a few examples: + // + // * `done:true` - The operation is complete. + // * `(metadata.@type=` \ + // `type.googleapis.com/google.spanner.admin.instance.v1.CreateInstancePartitionMetadata) + // AND` \ + // `(metadata.instance_partition.name:custom-instance-partition) AND` \ + // `(metadata.start_time < \"2021-03-28T14:50:00Z\") AND` \ + // `(error:*)` - Return operations where: + // * The operation's metadata type is + // [CreateInstancePartitionMetadata][google.spanner.admin.instance.v1.CreateInstancePartitionMetadata]. + // * The instance partition name contains "custom-instance-partition". + // * The operation started before 2021-03-28T14:50:00Z. + // * The operation resulted in an error. + string filter = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Number of operations to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.admin.instance.v1.ListInstancePartitionOperationsResponse.next_page_token] + // from a previous + // [ListInstancePartitionOperationsResponse][google.spanner.admin.instance.v1.ListInstancePartitionOperationsResponse] + // to the same `parent` and with the same `filter`. + string page_token = 4 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Deadline used while retrieving metadata for instance partition + // operations. Instance partitions whose operation metadata cannot be + // retrieved within this deadline will be added to + // [unreachable_instance_partitions][google.spanner.admin.instance.v1.ListInstancePartitionOperationsResponse.unreachable_instance_partitions] + // in + // [ListInstancePartitionOperationsResponse][google.spanner.admin.instance.v1.ListInstancePartitionOperationsResponse]. + google.protobuf.Timestamp instance_partition_deadline = 5 + [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for +// [ListInstancePartitionOperations][google.spanner.admin.instance.v1.InstanceAdmin.ListInstancePartitionOperations]. +message ListInstancePartitionOperationsResponse { + // The list of matching instance partition long-running operations. Each + // operation's name will be + // prefixed by the instance partition's name. The operation's + // metadata field type + // `metadata.type_url` describes the type of the metadata. + repeated google.longrunning.Operation operations = 1; + + // `next_page_token` can be sent in a subsequent + // [ListInstancePartitionOperations][google.spanner.admin.instance.v1.InstanceAdmin.ListInstancePartitionOperations] + // call to fetch more of the matching metadata. + string next_page_token = 2; + + // The list of unreachable instance partitions. + // It includes the names of instance partitions whose operation metadata could + // not be retrieved within + // [instance_partition_deadline][google.spanner.admin.instance.v1.ListInstancePartitionOperationsRequest.instance_partition_deadline]. + repeated string unreachable_instance_partitions = 3; +} + +// The request for +// [MoveInstance][google.spanner.admin.instance.v1.InstanceAdmin.MoveInstance]. +message MoveInstanceRequest { + // Required. The instance to move. + // Values are of the form `projects//instances/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Instance" + } + ]; + + // Required. The target instance configuration where to move the instance. + // Values are of the form `projects//instanceConfigs/`. + string target_config = 2 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/InstanceConfig" + } + ]; +} + +// The response for +// [MoveInstance][google.spanner.admin.instance.v1.InstanceAdmin.MoveInstance]. +message MoveInstanceResponse {} + +// Metadata type for the operation returned by +// [MoveInstance][google.spanner.admin.instance.v1.InstanceAdmin.MoveInstance]. +message MoveInstanceMetadata { + // The target instance configuration where to move the instance. + // Values are of the form `projects//instanceConfigs/`. + string target_config = 1; + + // The progress of the + // [MoveInstance][google.spanner.admin.instance.v1.InstanceAdmin.MoveInstance] + // operation. + // [progress_percent][google.spanner.admin.instance.v1.OperationProgress.progress_percent] + // is reset when cancellation is requested. + OperationProgress progress = 2; + + // The time at which this operation was cancelled. + google.protobuf.Timestamp cancel_time = 3; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/BUILD.bazel new file mode 100644 index 00000000..4bbf9911 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/BUILD.bazel @@ -0,0 +1,202 @@ +# This file was automatically generated by BuildFileGenerator +# https://github.com/googleapis/rules_gapic/tree/master/bazel + +# Most of the manual changes to this file will be overwritten. +# It's **only** allowed to change the following rule attribute values: +# - names of *_gapic_assembly_* rules +# - certain parameters of *_gapic_library rules, including but not limited to: +# * extra_protoc_parameters +# * extra_protoc_file_parameters +# The complete list of preserved parameters can be found in the source code. + +load("@com_google_googleapis_imports//:imports.bzl", "go_gapic_assembly_pkg", "go_gapic_library", "go_proto_library", "java_gapic_assembly_gradle_pkg", "java_gapic_library", "java_gapic_test", "java_grpc_library", "java_proto_library", "nodejs_gapic_assembly_pkg", "nodejs_gapic_library", "proto_library_with_info", "py_gapic_assembly_pkg", "py_gapic_library", "py_proto_library", "py_test") + +############################################################################## +# Common +############################################################################## +load("@rules_proto//proto:defs.bzl", "proto_library") + +# This is an API workspace, having public visibility by default makes perfect sense. +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "executor_proto", + srcs = [ + "cloud_executor.proto", + ], + deps = [ + "//google/api:client_proto", + "//google/api:field_behavior_proto", + "//google/longrunning:operations_proto", + "//google/rpc:status_proto", + "//google/spanner/admin/database/v1:database_proto", + "//google/spanner/admin/instance/v1:instance_proto", + "//google/spanner/v1:spanner_proto", + "@com_google_protobuf//:timestamp_proto", + ], +) + +proto_library_with_info( + name = "executor_proto_with_info", + deps = [ + ":executor_proto", + "//google/cloud:common_resources_proto", + ], +) + +java_proto_library( + name = "executor_java_proto", + deps = [":executor_proto"], +) + +java_grpc_library( + name = "executor_java_grpc", + srcs = [":executor_proto"], + deps = [":executor_java_proto"], +) + +java_gapic_library( + name = "executor_java_gapic", + srcs = [":executor_proto_with_info"], + gapic_yaml = "spanner_cloud_executor_gapic.yaml", + grpc_service_config = "spanner_cloud_executor_grpc_service_config.json", + service_yaml = "spanner_cloud_executor.yaml", + test_deps = [ + ":executor_java_grpc", + ], + transport = "grpc", + deps = [ + ":executor_java_proto", + "//google/api:api_java_proto", + "//google/spanner/admin/database/v1:database_java_proto", + ], +) + +java_gapic_test( + name = "executor_java_gapic_test_suite", + test_classes = [ + "com.google.cloud.spanner.executor.v1.SpannerExecutorProxyClientTest", + ], + runtime_deps = [":executor_java_gapic_test"], +) + +# Open Source Packages +java_gapic_assembly_gradle_pkg( + name = "google-cloud-spanner-executor-v1-java", + include_samples = True, + transport = "grpc", + deps = [ + ":executor_java_gapic", + ":executor_java_grpc", + ":executor_java_proto", + ":executor_proto", + ], +) + +go_proto_library( + name = "executor_go_proto", + compilers = ["@io_bazel_rules_go//proto:go_grpc"], + importpath = "cloud.google.com/go/spanner/executor/apiv1/executorpb", + protos = [":executor_proto"], + deps = [ + "//google/api:annotations_go_proto", + "//google/longrunning:longrunning_go_proto", + "//google/rpc:status_go_proto", + "//google/spanner/admin/database/v1:database_go_proto", + "//google/spanner/admin/instance/v1:instance_go_proto", + "//google/spanner/v1:spanner_go_proto", + ], +) + +go_gapic_library( + name = "executor_go_gapic", + srcs = [":executor_proto_with_info"], + grpc_service_config = "spanner_cloud_executor_grpc_service_config.json", + importpath = "cloud.google.com/go/spanner/executor/apiv1;executor", + metadata = True, + release_level = "beta", + service_yaml = "spanner_cloud_executor.yaml", + transport = "grpc", + deps = [ + ":executor_go_proto", + "//google/longrunning:longrunning_go_proto", + "//google/spanner/admin/database/v1:database_go_proto", + "@com_google_cloud_go_longrunning//:go_default_library", + "@com_google_cloud_go_longrunning//autogen:go_default_library", + ], +) + +# Open Source Packages +go_gapic_assembly_pkg( + name = "gapi-cloud-spanner-executor-v1-go", + deps = [ + ":executor_go_gapic", + ":executor_go_gapic_srcjar-metadata.srcjar", + ":executor_go_gapic_srcjar-snippets.srcjar", + ":executor_go_gapic_srcjar-test.srcjar", + ":executor_go_proto", + ], +) + +py_proto_library( + name = "executor_py_proto", + deps = [":executor_proto"], +) + +py_gapic_library( + name = "executor_py_gapic", + srcs = [":executor_proto"], + grpc_service_config = "spanner_cloud_executor_grpc_service_config.json", + service_yaml = "spanner_cloud_executor.yaml", + transport = "grpc", + deps = [ + ":executor_py_proto", + ], +) + +py_test( + name = "executor_py_gapic_test", + srcs = [ + "executor_py_gapic_pytest.py", + "executor_py_gapic_test.py", + ], + legacy_create_init = False, + deps = [ + ":executor_py_gapic", + ], +) + +# Open Source Packages +py_gapic_assembly_pkg( + name = "spanner-executor-v1-py", + deps = [ + ":executor_py_gapic", + ], +) + +nodejs_gapic_library( + name = "executor_nodejs_gapic", + package_name = "@google-cloud/executor", + src = ":executor_proto_with_info", + extra_protoc_parameters = [ + "metadata", + "template=typescript_gapic", + ], + grpc_service_config = "spanner_cloud_executor_grpc_service_config.json", + package = "google.spanner.executor.v1", + service_yaml = "spanner_cloud_executor.yaml", + transport = "grpc", + deps = [ + ], +) + +nodejs_gapic_assembly_pkg( + name = "spanner-executor-v1-nodejs", + deps = [ + ":executor_nodejs_gapic", + ":executor_proto", + "//google/spanner/admin/database/v1:database_proto", + "//google/spanner/admin/instance/v1:instance_proto", + "//google/spanner/v1:spanner_proto", + ], +) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/README.md b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/README.md new file mode 100644 index 00000000..8183a8af --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/README.md @@ -0,0 +1,16 @@ +# Cloud Spanner Executor test API + + +Stop. This folder is likely not what you are looking for. + +This folder contains protocol buffer definitions for test-only API for +Cloud Spanner clients. Unless told otherwise by a Google Cloud representative, +do not use any of the contents of this folder. If you would like to use Cloud +Spanner, please consult our +[official documentation](https://cloud.google.com/spanner/docs/apis) for details +on our APIs, or else consider one of our +[client libraries](https://cloud.google.com/spanner/docs/reference/libraries). + +This API defined in this folder is unreleased and may shut off, break, or fail +at any time for any users who are not registered as a part of a private preview +program. diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/cloud_executor.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/cloud_executor.proto new file mode 100644 index 00000000..cf68519e --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/cloud_executor.proto @@ -0,0 +1,1520 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.executor.v1; + +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; +import "google/spanner/admin/database/v1/backup.proto"; +import "google/spanner/admin/database/v1/common.proto"; +import "google/spanner/admin/database/v1/spanner_database_admin.proto"; +import "google/spanner/admin/instance/v1/spanner_instance_admin.proto"; +import "google/spanner/v1/spanner.proto"; +import "google/spanner/v1/type.proto"; + +option go_package = "cloud.google.com/go/spanner/executor/apiv1/executorpb;executorpb"; +option java_multiple_files = true; +option java_outer_classname = "CloudExecutorProto"; +option java_package = "com.google.spanner.executor.v1"; + +// Service that executes SpannerActions asynchronously. +service SpannerExecutorProxy { + option (google.api.default_host) = "spanner-cloud-executor.googleapis.com"; + + // ExecuteActionAsync is a streaming call that starts executing a new Spanner + // action. + // + // For each request, the server will reply with one or more responses, but + // only the last response will contain status in the outcome. + // + // Responses can be matched to requests by action_id. It is allowed to have + // multiple actions in flight--in that case, actions are be executed in + // parallel. + rpc ExecuteActionAsync(stream SpannerAsyncActionRequest) + returns (stream SpannerAsyncActionResponse) {} +} + +// Request to executor service that start a new Spanner action. +message SpannerAsyncActionRequest { + // Action id to uniquely identify this action request. + int32 action_id = 1; + + // The actual SpannerAction to perform. + SpannerAction action = 2; +} + +// Response from executor service. +message SpannerAsyncActionResponse { + // Action id corresponds to the request. + int32 action_id = 1; + + // If action results are split into multiple responses, only the last response + // can and should contain status. + SpannerActionOutcome outcome = 2; +} + +// SpannerAction defines a primitive action that can be performed against +// Spanner, such as begin or commit a transaction, or perform a read or +// mutation. +message SpannerAction { + // Database against which to perform action. + // In a context where a series of actions take place, an action may omit + // database path if it applies to the same database as the previous action. + string database_path = 1; + + // Configuration options for Spanner backend + SpannerOptions spanner_options = 2; + + // Action represents a spanner action kind, there will only be one action kind + // per SpannerAction. + oneof action { + // Action to start a transaction. + StartTransactionAction start = 10; + + // Action to finish a transaction. + FinishTransactionAction finish = 11; + + // Action to do a normal read. + ReadAction read = 20; + + // Action to do a query. + QueryAction query = 21; + + // Action to buffer a mutation. + MutationAction mutation = 22; + + // Action to a DML. + DmlAction dml = 23; + + // Action to a batch DML. + BatchDmlAction batch_dml = 24; + + // Action to write a mutation. + WriteMutationsAction write = 25; + + // Action to a partitioned update. + PartitionedUpdateAction partitioned_update = 27; + + // Action that contains any administrative operation, like database, + // instance manipulation. + AdminAction admin = 30; + + // Action to start a batch transaction. + StartBatchTransactionAction start_batch_txn = 40; + + // Action to close a batch transaction. + CloseBatchTransactionAction close_batch_txn = 41; + + // Action to generate database partitions for batch read. + GenerateDbPartitionsForReadAction generate_db_partitions_read = 42; + + // Action to generate database partitions for batch query. + GenerateDbPartitionsForQueryAction generate_db_partitions_query = 43; + + // Action to execute batch actions on generated partitions. + ExecutePartitionAction execute_partition = 44; + + // Action to execute change stream query. + ExecuteChangeStreamQuery execute_change_stream_query = 50; + + // Query cancellation action for testing the cancellation of a query. + QueryCancellationAction query_cancellation = 51; + } +} + +// A single read request. +message ReadAction { + // The table to read at. + string table = 1; + + // The index to read at if it's an index read. + optional string index = 2; + + // List of columns must begin with the key columns used for the read. + repeated string column = 3; + + // Keys for performing this read. + KeySet keys = 4; + + // Limit on number of rows to read. If set, must be positive. + int32 limit = 5; +} + +// A SQL query request. +message QueryAction { + // Parameter that bind to placeholders in the SQL string + message Parameter { + // Name of the parameter (with no leading @). + string name = 1; + + // Type of the parameter. + google.spanner.v1.Type type = 2; + + // Value of the parameter. + Value value = 3; + } + + // The SQL string. + string sql = 1; + + // Parameters for the SQL string. + repeated Parameter params = 2; +} + +// A single DML statement. +message DmlAction { + // DML statement. + QueryAction update = 1; + + // Whether to autocommit the transaction after executing the DML statement, + // if the Executor supports autocommit. + optional bool autocommit_if_supported = 2; +} + +// Batch of DML statements invoked using batched execution. +message BatchDmlAction { + // DML statements. + repeated QueryAction updates = 1; +} + +// Value represents a single value that can be read or written to/from +// Spanner. +message Value { + // Exactly one of the following fields will be present. + oneof value_type { + // If is_null is set, then this value is null. + bool is_null = 1; + + // Int type value. It's used for all integer number types, like int32 and + // int64. + int64 int_value = 2; + + // Bool type value. + bool bool_value = 3; + + // Double type value. It's used for all float point types, like float and + // double. + double double_value = 4; + + // Bytes type value, stored in CORD. It's also used for PROTO type value. + bytes bytes_value = 5; + + // String type value, stored in CORD. + string string_value = 6; + + // Struct type value. It contains a ValueList representing the values in + // this struct. + ValueList struct_value = 7; + + // Timestamp type value. + google.protobuf.Timestamp timestamp_value = 8; + + // Date type value. Date is specified as a number of days since Unix epoch. + int32 date_days_value = 9; + + // If set, holds the sentinel value for the transaction CommitTimestamp. + bool is_commit_timestamp = 10; + + // Array type value. The underlying Valuelist should have values that have + // the same type. + ValueList array_value = 11; + } + + // Type of array element. Only set if value is an array. + optional google.spanner.v1.Type array_type = 12; +} + +// KeyRange represents a range of rows in a table or index. +// +// A range has a start key and an end key. These keys can be open or +// closed, indicating if the range includes rows with that key. +// +// Keys are represented by "ValueList", where the ith value in the list +// corresponds to the ith component of the table or index primary key. +message KeyRange { + // Type controls whether "start" and "limit" are open or closed. By default, + // "start" is closed, and "limit" is open. + enum Type { + // "TYPE_UNSPECIFIED" is equivalent to "CLOSED_OPEN". + TYPE_UNSPECIFIED = 0; + + // [start,limit] + CLOSED_CLOSED = 1; + + // [start,limit) + CLOSED_OPEN = 2; + + // (start,limit] + OPEN_CLOSED = 3; + + // (start,limit) + OPEN_OPEN = 4; + } + + // "start" and "limit" must have the same number of key parts, + // though they may name only a prefix of the table or index key. + // The start key of this KeyRange. + ValueList start = 1; + + // The end key of this KeyRange. + ValueList limit = 2; + + // "start" and "limit" type for this KeyRange. + optional Type type = 3; +} + +// KeySet defines a collection of Spanner keys and/or key ranges. All +// the keys are expected to be in the same table. The keys need not be +// sorted in any particular way. +message KeySet { + // A list of specific keys. Entries in "keys" should have exactly as + // many elements as there are columns in the primary or index key + // with which this "KeySet" is used. + repeated ValueList point = 1; + + // A list of key ranges. + repeated KeyRange range = 2; + + // For convenience "all" can be set to "true" to indicate that this + // "KeySet" matches all keys in the table or index. Note that any keys + // specified in "keys" or "ranges" are only yielded once. + bool all = 3; +} + +// List of values. +message ValueList { + // Values contained in this ValueList. + repeated Value value = 1; +} + +// A single mutation request. +message MutationAction { + // Arguments to Insert, InsertOrUpdate, and Replace operations. + message InsertArgs { + // The names of the columns to be written. + repeated string column = 1; + + // Type information for the "values" entries below. + repeated google.spanner.v1.Type type = 2; + + // The values to be written. + repeated ValueList values = 3; + } + + // Arguments to Update. + message UpdateArgs { + // The columns to be updated. Identical to InsertArgs.column. + repeated string column = 1; + + // Type information for "values". Identical to InsertArgs.type. + repeated google.spanner.v1.Type type = 2; + + // The values to be updated. Identical to InsertArgs.values. + repeated ValueList values = 3; + } + + // Mod represents the write action that will be perform to a table. Each mod + // will specify exactly one action, from insert, update, insert_or_update, + // replace and delete. + message Mod { + // The table to write. + string table = 1; + + // Exactly one of the remaining elements may be present. + // Insert new rows into "table". + InsertArgs insert = 2; + + // Update columns stored in existing rows of "table". + UpdateArgs update = 3; + + // Insert or update existing rows of "table". + InsertArgs insert_or_update = 4; + + // Replace existing rows of "table". + InsertArgs replace = 5; + + // Delete rows from "table". + KeySet delete_keys = 6; + } + + // Mods that contained in this mutation. + repeated Mod mod = 1; +} + +// WriteMutationAction defines an action of flushing the mutation so they +// are visible to subsequent operations in the transaction. +message WriteMutationsAction { + // The mutation to write. + MutationAction mutation = 1; +} + +// PartitionedUpdateAction defines an action to execute a partitioned DML +// which runs different partitions in parallel. +message PartitionedUpdateAction { + message ExecutePartitionedUpdateOptions { + // RPC Priority + optional google.spanner.v1.RequestOptions.Priority rpc_priority = 1; + + // Transaction tag + optional string tag = 2; + } + + // Options for partitioned update. + optional ExecutePartitionedUpdateOptions options = 1; + + // Partitioned dml query. + QueryAction update = 2; +} + +// StartTransactionAction defines an action of initializing a transaction. +message StartTransactionAction { + // Concurrency is for read-only transactions and must be omitted for + // read-write transactions. + optional Concurrency concurrency = 1; + + // Metadata about tables and columns that will be involved in this + // transaction. It is to convert values of key parts correctly. + repeated TableMetadata table = 2; + + // Transaction_seed contains workid and op pair for this transaction, used for + // testing. + string transaction_seed = 3; + + // Execution options (e.g., whether transaction is opaque, optimistic). + optional TransactionExecutionOptions execution_options = 4; +} + +// Concurrency for read-only transactions. +message Concurrency { + // Concurrency mode set for read-only transactions, exactly one mode below + // should be set. + oneof concurrency_mode { + // Indicates a read at a consistent timestamp that is specified relative to + // now. That is, if the caller has specified an exact staleness of s + // seconds, we will read at now - s. + double staleness_seconds = 1; + + // Indicates a boundedly stale read that reads at a timestamp >= T. + int64 min_read_timestamp_micros = 2; + + // Indicates a boundedly stale read that is at most N seconds stale. + double max_staleness_seconds = 3; + + // Indicates a read at a consistent timestamp. + int64 exact_timestamp_micros = 4; + + // Indicates a strong read, must only be set to true, or unset. + bool strong = 5; + + // Indicates a batch read, must only be set to true, or unset. + bool batch = 6; + } + + // True if exact_timestamp_micros is set, and the chosen timestamp is that of + // a snapshot epoch. + bool snapshot_epoch_read = 7; + + // If set, this is a snapshot epoch read constrained to read only the + // specified log scope root table, and its children. Will not be set for full + // database epochs. + string snapshot_epoch_root_table = 8; + + // Set only when batch is true. + int64 batch_read_timestamp_micros = 9; +} + +// TableMetadata contains metadata of a single table. +message TableMetadata { + // Table name. + string name = 1; + + // Columns, in the same order as in the schema. + repeated ColumnMetadata column = 2; + + // Keys, in order. Column name is currently not populated. + repeated ColumnMetadata key_column = 3; +} + +// ColumnMetadata represents metadata of a single column. +message ColumnMetadata { + // Column name. + string name = 1; + + // Column type. + google.spanner.v1.Type type = 2; +} + +// Options for executing the transaction. +message TransactionExecutionOptions { + // Whether optimistic concurrency should be used to execute this transaction. + bool optimistic = 1; +} + +// FinishTransactionAction defines an action of finishing a transaction. +message FinishTransactionAction { + // Mode indicates how the transaction should be finished. + enum Mode { + // "MODE_UNSPECIFIED" is equivalent to "COMMIT". + MODE_UNSPECIFIED = 0; + + // Commit the transaction. + COMMIT = 1; + + // Drop the transaction without committing it. + ABANDON = 2; + } + + // Defines how exactly the transaction should be completed, e.g. with + // commit or abortion. + Mode mode = 1; +} + +// AdminAction defines all the cloud spanner admin actions, including +// instance/database admin ops, backup ops and operation actions. +message AdminAction { + // Exactly one of the actions below will be performed in AdminAction. + oneof action { + // Action that creates a user instance config. + CreateUserInstanceConfigAction create_user_instance_config = 1; + + // Action that updates a user instance config. + UpdateUserInstanceConfigAction update_user_instance_config = 2; + + // Action that deletes a user instance config. + DeleteUserInstanceConfigAction delete_user_instance_config = 3; + + // Action that gets a user instance config. + GetCloudInstanceConfigAction get_cloud_instance_config = 4; + + // Action that lists user instance configs. + ListCloudInstanceConfigsAction list_instance_configs = 5; + + // Action that creates a Cloud Spanner instance. + CreateCloudInstanceAction create_cloud_instance = 6; + + // Action that updates a Cloud Spanner instance. + UpdateCloudInstanceAction update_cloud_instance = 7; + + // Action that deletes a Cloud Spanner instance. + DeleteCloudInstanceAction delete_cloud_instance = 8; + + // Action that lists Cloud Spanner instances. + ListCloudInstancesAction list_cloud_instances = 9; + + // Action that retrieves a Cloud Spanner instance. + GetCloudInstanceAction get_cloud_instance = 10; + + // Action that creates a Cloud Spanner database. + CreateCloudDatabaseAction create_cloud_database = 11; + + // Action that updates the schema of a Cloud Spanner database. + UpdateCloudDatabaseDdlAction update_cloud_database_ddl = 12; + + // Action that updates the schema of a Cloud Spanner database. + UpdateCloudDatabaseAction update_cloud_database = 27; + + // Action that drops a Cloud Spanner database. + DropCloudDatabaseAction drop_cloud_database = 13; + + // Action that lists Cloud Spanner databases. + ListCloudDatabasesAction list_cloud_databases = 14; + + // Action that lists Cloud Spanner database operations. + ListCloudDatabaseOperationsAction list_cloud_database_operations = 15; + + // Action that restores a Cloud Spanner database from a backup. + RestoreCloudDatabaseAction restore_cloud_database = 16; + + // Action that gets a Cloud Spanner database. + GetCloudDatabaseAction get_cloud_database = 17; + + // Action that creates a Cloud Spanner database backup. + CreateCloudBackupAction create_cloud_backup = 18; + + // Action that copies a Cloud Spanner database backup. + CopyCloudBackupAction copy_cloud_backup = 19; + + // Action that gets a Cloud Spanner database backup. + GetCloudBackupAction get_cloud_backup = 20; + + // Action that updates a Cloud Spanner database backup. + UpdateCloudBackupAction update_cloud_backup = 21; + + // Action that deletes a Cloud Spanner database backup. + DeleteCloudBackupAction delete_cloud_backup = 22; + + // Action that lists Cloud Spanner database backups. + ListCloudBackupsAction list_cloud_backups = 23; + + // Action that lists Cloud Spanner database backup operations. + ListCloudBackupOperationsAction list_cloud_backup_operations = 24; + + // Action that gets an operation. + GetOperationAction get_operation = 25; + + // Action that cancels an operation. + CancelOperationAction cancel_operation = 26; + + // Action that changes quorum of a Cloud Spanner database. + ChangeQuorumCloudDatabaseAction change_quorum_cloud_database = 28; + } +} + +// Action that creates a user instance config. +message CreateUserInstanceConfigAction { + // User instance config ID (not path), e.g. "custom-config". + string user_config_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // Base config ID, e.g. "test-config". + string base_config_id = 3; + + // Replicas that should be included in the user config. + repeated google.spanner.admin.instance.v1.ReplicaInfo replicas = 4; +} + +// Action that updates a user instance config. +message UpdateUserInstanceConfigAction { + // User instance config ID (not path), e.g. "custom-config". + string user_config_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // The descriptive name for this instance config as it appears in UIs. + optional string display_name = 3; + + // labels. + map labels = 4; +} + +// Action that gets a user instance config. +message GetCloudInstanceConfigAction { + // Instance config ID (not path), e.g. "custom-config". + string instance_config_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; +} + +// Action that deletes a user instance configs. +message DeleteUserInstanceConfigAction { + // User instance config ID (not path), e.g. "custom-config". + string user_config_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; +} + +// Action that lists user instance configs. +message ListCloudInstanceConfigsAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Number of instance configs to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + optional int32 page_size = 2; + + // If non-empty, "page_token" should contain a next_page_token + // from a previous ListInstanceConfigsResponse to the same "parent". + optional string page_token = 3; +} + +// Action that creates a Cloud Spanner instance. +message CreateCloudInstanceAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // Instance config ID, e.g. "test-config". + string instance_config_id = 3; + + // Number of nodes (processing_units should not be set or set to 0 if used). + optional int32 node_count = 4; + + // Number of processing units (node_count should be set to 0 if used). + optional int32 processing_units = 6; + + // The autoscaling config for this instance. If non-empty, an autoscaling + // instance will be created (processing_units and node_count should be set to + // 0 if used). + optional google.spanner.admin.instance.v1.AutoscalingConfig + autoscaling_config = 7; + + // labels. + map labels = 5; +} + +// Action that updates a Cloud Spanner instance. +message UpdateCloudInstanceAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // The descriptive name for this instance as it appears in UIs. + // Must be unique per project and between 4 and 30 characters in length. + optional string display_name = 3; + + // The number of nodes allocated to this instance. At most one of either + // node_count or processing_units should be present in the message. + optional int32 node_count = 4; + + // The number of processing units allocated to this instance. At most one of + // processing_units or node_count should be present in the message. + optional int32 processing_units = 5; + + // The autoscaling config for this instance. If non-empty, this instance is + // using autoscaling (processing_units and node_count should be set to + // 0 if used). + optional google.spanner.admin.instance.v1.AutoscalingConfig + autoscaling_config = 7; + + // labels. + map labels = 6; +} + +// Action that deletes a Cloud Spanner instance. +message DeleteCloudInstanceAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; +} + +// Action that creates a Cloud Spanner database. +message CreateCloudDatabaseAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // Cloud database ID (not full path), e.g. "db0". + string database_id = 3; + + // SDL statements to apply to the new database. + repeated string sdl_statement = 4; + + // The KMS key used to encrypt the database to be created if the database + // should be CMEK protected. + google.spanner.admin.database.v1.EncryptionConfig encryption_config = 5; + + // Optional SQL dialect (GOOGLESQL or POSTGRESQL). Default: GOOGLESQL. + optional string dialect = 6; + + optional bytes proto_descriptors = 7; +} + +// Action that updates the schema of a Cloud Spanner database. +message UpdateCloudDatabaseDdlAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // Cloud database ID (not full path), e.g. "db0". + string database_id = 3; + + // SDL statements to apply to the database. + repeated string sdl_statement = 4; + + // Op ID can be used to track progress of the update. If set, it must be + // unique per database. If not set, Cloud Spanner will generate operation ID + // automatically. + string operation_id = 5; + + optional bytes proto_descriptors = 6; +} + +// Action that updates a Cloud Spanner database. +message UpdateCloudDatabaseAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // Cloud database name (not full path), e.g. "db0". + string database_name = 3; + + // Updated value of enable_drop_protection, this is the only field that has + // supported to be updated. + bool enable_drop_protection = 4; +} + +// Action that drops a Cloud Spanner database. +message DropCloudDatabaseAction { + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 1; + + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 2; + + // Cloud database ID (not full path), e.g. "db0". + string database_id = 3; +} + +// Action that changes quorum of a Cloud Spanner database. +message ChangeQuorumCloudDatabaseAction { + // The fully qualified uri of the database whose quorum has to be changed. + optional string database_uri = 1; + + // The locations of the serving regions, e.g. "asia-south1". + repeated string serving_locations = 2; +} + +// Action that lists Cloud Spanner databases. +message ListCloudDatabasesAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path) to list databases from, e.g. "test-instance". + string instance_id = 2; + + // Number of databases to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 3; + + // If non-empty, "page_token" should contain a next_page_token + // from a previous ListDatabasesResponse to the same "parent" + // and with the same "filter". + string page_token = 4; +} + +// Action that lists Cloud Spanner databases. +message ListCloudInstancesAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // A filter expression that filters what operations are returned in the + // response. + // The expression must specify the field name, a comparison operator, + // and the value that you want to use for filtering. + // Refer spanner_instance_admin.proto.ListInstancesRequest for + // detail. + optional string filter = 2; + + // Number of instances to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + optional int32 page_size = 3; + + // If non-empty, "page_token" should contain a next_page_token + // from a previous ListInstancesResponse to the same "parent" + // and with the same "filter". + optional string page_token = 4; +} + +// Action that retrieves a Cloud Spanner instance. +message GetCloudInstanceAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path) to retrieve the instance from, + // e.g. "test-instance". + string instance_id = 2; +} + +// Action that lists Cloud Spanner database operations. +message ListCloudDatabaseOperationsAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path) to list database operations from, + // e.g. "test-instance". + string instance_id = 2; + + // A filter expression that filters what operations are returned in the + // response. + // The expression must specify the field name, a comparison operator, + // and the value that you want to use for filtering. + // Refer spanner_database_admin.proto.ListDatabaseOperationsRequest for + // detail. + string filter = 3; + + // Number of databases to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 4; + + // If non-empty, "page_token" should contain a next_page_token + // from a previous ListDatabaseOperationsResponse to the same "parent" + // and with the same "filter". + string page_token = 5; +} + +// Action that restores a Cloud Spanner database from a backup. +message RestoreCloudDatabaseAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path) containing the backup, e.g. "backup-instance". + string backup_instance_id = 2; + + // The id of the backup from which to restore, e.g. "test-backup". + string backup_id = 3; + + // Cloud instance ID (not path) containing the database, e.g. + // "database-instance". + string database_instance_id = 4; + + // The id of the database to create and restore to, e.g. "db0". Note that this + // database must not already exist. + string database_id = 5; + + // The KMS key(s) used to encrypt the restored database to be created if the + // restored database should be CMEK protected. + google.spanner.admin.database.v1.EncryptionConfig encryption_config = 7; +} + +// Action that gets a Cloud Spanner database. +message GetCloudDatabaseAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 2; + + // The id of the database to get, e.g. "db0". + string database_id = 3; +} + +// Action that creates a Cloud Spanner database backup. +message CreateCloudBackupAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 2; + + // The id of the backup to be created, e.g. "test-backup". + string backup_id = 3; + + // The id of the database from which this backup was + // created, e.g. "db0". Note that this needs to be in the + // same instance as the backup. + string database_id = 4; + + // Output only. The expiration time of the backup, which must be at least 6 + // hours and at most 366 days from the time the request is received. + google.protobuf.Timestamp expire_time = 5 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The version time of the backup, which must be within the time range of + // [earliest_version_time, NOW], where earliest_version_time is retrieved by + // cloud spanner frontend API (See details: go/cs-pitr-lite-design). + optional google.protobuf.Timestamp version_time = 6; + + // The KMS key(s) used to encrypt the backup to be created if the backup + // should be CMEK protected. + google.spanner.admin.database.v1.EncryptionConfig encryption_config = 7; +} + +// Action that copies a Cloud Spanner database backup. +message CopyCloudBackupAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 2; + + // The id of the backup to be created, e.g. "test-backup". + string backup_id = 3; + + // The fully qualified uri of the source backup from which this + // backup was copied. eg. + // "projects//instances//backups/". + string source_backup = 4; + + // Output only. The expiration time of the backup, which must be at least 6 + // hours and at most 366 days from the time the request is received. + google.protobuf.Timestamp expire_time = 5 + [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// Action that gets a Cloud Spanner database backup. +message GetCloudBackupAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 2; + + // The id of the backup to get, e.g. "test-backup". + string backup_id = 3; +} + +// Action that updates a Cloud Spanner database backup. +message UpdateCloudBackupAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 2; + + // The id of the backup to update, e.g. "test-backup". + string backup_id = 3; + + // Output only. Updated value of expire_time, this is the only field + // that supported to be updated. + google.protobuf.Timestamp expire_time = 4 + [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// Action that deletes a Cloud Spanner database backup. +message DeleteCloudBackupAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path), e.g. "test-instance". + string instance_id = 2; + + // The id of the backup to delete, e.g. "test-backup". + string backup_id = 3; +} + +// Action that lists Cloud Spanner database backups. +message ListCloudBackupsAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path) to list backups from, e.g. "test-instance". + string instance_id = 2; + + // A filter expression that filters backups listed in the response. + // The expression must specify the field name, a comparison operator, + // and the value that you want to use for filtering. + // Refer backup.proto.ListBackupsRequest for detail. + string filter = 3; + + // Number of backups to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 4; + + // If non-empty, "page_token" should contain a next_page_token + // from a previous ListBackupsResponse to the same "parent" + // and with the same "filter". + string page_token = 5; +} + +// Action that lists Cloud Spanner database backup operations. +message ListCloudBackupOperationsAction { + // Cloud project ID, e.g. "spanner-cloud-systest". + string project_id = 1; + + // Cloud instance ID (not path) to list backup operations from, + // e.g. "test-instance". + string instance_id = 2; + + // A filter expression that filters what operations are returned in the + // response. + // The expression must specify the field name, a comparison operator, + // and the value that you want to use for filtering. + // Refer backup.proto.ListBackupOperationsRequest for detail. + string filter = 3; + + // Number of backups to be returned in the response. If 0 or + // less, defaults to the server's maximum allowed page size. + int32 page_size = 4; + + // If non-empty, "page_token" should contain a next_page_token + // from a previous ListBackupOperationsResponse to the same "parent" + // and with the same "filter". + string page_token = 5; +} + +// Action that gets an operation. +message GetOperationAction { + // The name of the operation resource. + string operation = 1; +} + +// Query cancellation action defines the long running query and the cancel query +// format depening on the Cloud database dialect. +message QueryCancellationAction { + // Long running query. + string long_running_sql = 1; + + // Format of the cancel query for the cloud database dialect. + string cancel_query = 2; +} + +// Action that cancels an operation. +message CancelOperationAction { + // The name of the operation resource to be cancelled. + string operation = 1; +} + +// Starts a batch read-only transaction in executor. Successful outcomes of this +// action will contain batch_txn_id--the identificator that can be used to start +// the same transaction in other Executors to parallelize partition processing. +// +// Example of a batch read flow: +// 1. Start batch transaction with a timestamp (StartBatchTransactionAction) +// 2. Generate database partitions for a read or query +// (GenerateDbPartitionsForReadAction/GenerateDbPartitionsForQueryAction) +// 3. Call ExecutePartitionAction for some or all partitions, process rows +// 4. Clean up the transaction (CloseBatchTransactionAction). +// +// More sophisticated example, with parallel processing: +// 1. Start batch transaction with a timestamp (StartBatchTransactionAction), +// note the returned BatchTransactionId +// 2. Generate database partitions for a read or query +// (GenerateDbPartitionsForReadAction/GenerateDbPartitionsForQueryAction) +// 3. Distribute the partitions over a pool of workers, along with the +// transaction ID. +// +// In each worker: +// 4-1. StartBatchTransactionAction with the given transaction ID +// 4-2. ExecutePartitionAction for each partition it got, process read results +// 4-3. Close (not cleanup) the transaction (CloseBatchTransactionAction). +// +// When all workers are done: +// 5. Cleanup the transaction (CloseBatchTransactionAction). This can be done +// either by the last worker to finish the job, or by the main Executor that +// initialized this transaction in the first place. It is also possible to clean +// it up with a brand new Executor -- just execute StartBatchTransactionAction +// with the ID, then clean it up right away. +// +// Cleaning up is optional, but recommended. +message StartBatchTransactionAction { + // To start a new transaction, specify an exact timestamp. Alternatively, an + // existing batch transaction ID can be used. Either one of two must be + // set. + oneof param { + // The exact timestamp to start the batch transaction. + google.protobuf.Timestamp batch_txn_time = 1; + + // ID of a batch read-only transaction. It can be used to start the same + // batch transaction on multiple executors and parallelize partition + // processing. + bytes tid = 2; + } + + // Database role to assume while performing this action. Setting the + // database_role will enforce additional role-based access checks on this + // action. + string cloud_database_role = 3; +} + +// Closes or cleans up the currently opened batch read-only transaction. +// +// Once a transaction is closed, the Executor can be disposed of or used to +// start start another transaction. Closing a batch transaction in one Executor +// doesn't affect the transaction's state in other Executors that also read from +// it. +// +// When a transaction is cleaned up, it becomes globally invalid. Cleaning up is +// optional, but recommended. +message CloseBatchTransactionAction { + // Indicates whether the transaction needs to be cleaned up. + bool cleanup = 1; +} + +// Generate database partitions for the given read. Successful outcomes will +// contain database partitions in the db_partition field. +message GenerateDbPartitionsForReadAction { + // Read to generate partitions for. + ReadAction read = 1; + + // Metadata related to the tables involved in the read. + repeated TableMetadata table = 2; + + // Desired size of data in each partition. Spanner doesn't guarantee to + // respect this value. + optional int64 desired_bytes_per_partition = 3; + + // If set, the desired max number of partitions. Spanner doesn't guarantee to + // respect this value. + optional int64 max_partition_count = 4; +} + +// Generate database partitions for the given query. Successful outcomes will +// contain database partitions in the db_partition field. +message GenerateDbPartitionsForQueryAction { + // Query to generate partitions for. + QueryAction query = 1; + + // Desired size of data in each partition. Spanner doesn't guarantee to + // respect this value. + optional int64 desired_bytes_per_partition = 2; +} + +// Identifies a database partition generated for a particular read or query. To +// read rows from the partition, use ExecutePartitionAction. +message BatchPartition { + // Serialized Partition instance. + bytes partition = 1; + + // The partition token decrypted from partition. + bytes partition_token = 2; + + // Table name is set iff the partition was generated for a read (as opposed to + // a query). + optional string table = 3; + + // Index name if the partition was generated for an index read. + optional string index = 4; +} + +// Performs a read or query for the given partitions. This action must be +// executed in the context of the same transaction that was used to generate +// given partitions. +message ExecutePartitionAction { + // Batch partition to execute on. + BatchPartition partition = 1; +} + +// Execute a change stream TVF query. +message ExecuteChangeStreamQuery { + // Name for this change stream. + string name = 1; + + // Specifies that records with commit_timestamp greater than or equal to + // start_time should be returned. + google.protobuf.Timestamp start_time = 2; + + // Specifies that records with commit_timestamp less than or equal to + // end_time should be returned. + optional google.protobuf.Timestamp end_time = 3; + + // Specifies which change stream partition to query, based on the content of + // child partitions records. + optional string partition_token = 4; + + // Read options for this change stream query. + repeated string read_options = 5; + + // Determines how frequently a heartbeat ChangeRecord will be returned in case + // there are no transactions committed in this partition, in milliseconds. + optional int32 heartbeat_milliseconds = 6; + + // Deadline for this change stream query, in seconds. + optional int64 deadline_seconds = 7; + + // Database role to assume while performing this action. This should only be + // set for cloud requests. Setting the database role will enforce additional + // role-based access checks on this action. + optional string cloud_database_role = 8; +} + +// SpannerActionOutcome defines a result of execution of a single SpannerAction. +message SpannerActionOutcome { + // If an outcome is split into multiple parts, status will be set only in the + // last part. + optional google.rpc.Status status = 1; + + // Transaction timestamp. It must be set for successful committed actions. + optional google.protobuf.Timestamp commit_time = 2; + + // Result of a ReadAction. This field must be set for ReadActions even if + // no rows were read. + optional ReadResult read_result = 3; + + // Result of a Query. This field must be set for Queries even if no rows were + // read. + optional QueryResult query_result = 4; + + // This bit indicates that Spanner has restarted the current transaction. It + // means that the client should replay all the reads and writes. + // Setting it to true is only valid in the context of a read-write + // transaction, as an outcome of a committing FinishTransactionAction. + optional bool transaction_restarted = 5; + + // In successful StartBatchTransactionAction outcomes, this contains the ID of + // the transaction. + optional bytes batch_txn_id = 6; + + // Generated database partitions (result of a + // GenetageDbPartitionsForReadAction/GenerateDbPartitionsForQueryAction). + repeated BatchPartition db_partition = 7; + + // Result of admin related actions. + optional AdminResult admin_result = 8; + + // Stores rows modified by query in single DML or batch DML action. + // In case of batch DML action, stores 0 as row count of errored DML query. + repeated int64 dml_rows_modified = 9; + + // Change stream records returned by a change stream query. + repeated ChangeStreamRecord change_stream_records = 10; +} + +// AdminResult contains admin action results, for database/backup/operation. +message AdminResult { + // Results of cloud backup related actions. + CloudBackupResponse backup_response = 1; + + // Results of operation related actions. + OperationResponse operation_response = 2; + + // Results of database related actions. + CloudDatabaseResponse database_response = 3; + + // Results of instance related actions. + CloudInstanceResponse instance_response = 4; + + // Results of instance config related actions. + CloudInstanceConfigResponse instance_config_response = 5; +} + +// CloudBackupResponse contains results returned by cloud backup related +// actions. +message CloudBackupResponse { + // List of backups returned by ListCloudBackupsAction. + repeated google.spanner.admin.database.v1.Backup listed_backups = 1; + + // List of operations returned by ListCloudBackupOperationsAction. + repeated google.longrunning.Operation listed_backup_operations = 2; + + // "next_page_token" can be sent in a subsequent list action + // to fetch more of the matching data. + string next_page_token = 3; + + // Backup returned by GetCloudBackupAction/UpdateCloudBackupAction. + google.spanner.admin.database.v1.Backup backup = 4; +} + +// OperationResponse contains results returned by operation related actions. +message OperationResponse { + // List of operations returned by ListOperationsAction. + repeated google.longrunning.Operation listed_operations = 1; + + // "next_page_token" can be sent in a subsequent list action + // to fetch more of the matching data. + string next_page_token = 2; + + // Operation returned by GetOperationAction. + google.longrunning.Operation operation = 3; +} + +// CloudInstanceResponse contains results returned by cloud instance related +// actions. +message CloudInstanceResponse { + // List of instances returned by ListCloudInstancesAction. + repeated google.spanner.admin.instance.v1.Instance listed_instances = 1; + + // "next_page_token" can be sent in a subsequent list action + // to fetch more of the matching data. + string next_page_token = 2; + + // Instance returned by GetCloudInstanceAction + google.spanner.admin.instance.v1.Instance instance = 3; +} + +// CloudInstanceConfigResponse contains results returned by cloud instance +// config related actions. +message CloudInstanceConfigResponse { + // List of instance configs returned by ListCloudInstanceConfigsAction. + repeated google.spanner.admin.instance.v1.InstanceConfig + listed_instance_configs = 1; + + // "next_page_token" can be sent in a subsequent list action + // to fetch more of the matching data. + string next_page_token = 2; + + // Instance config returned by GetCloudInstanceConfigAction. + google.spanner.admin.instance.v1.InstanceConfig instance_config = 3; +} + +// CloudDatabaseResponse contains results returned by cloud database related +// actions. +message CloudDatabaseResponse { + // List of databases returned by ListCloudDatabasesAction. + repeated google.spanner.admin.database.v1.Database listed_databases = 1; + + // List of operations returned by ListCloudDatabaseOperationsAction. + repeated google.longrunning.Operation listed_database_operations = 2; + + // "next_page_token" can be sent in a subsequent list action + // to fetch more of the matching data. + string next_page_token = 3; + + // Database returned by GetCloudDatabaseAction + google.spanner.admin.database.v1.Database database = 4; +} + +// ReadResult contains rows read. +message ReadResult { + // Table name. + string table = 1; + + // Index name, if read from an index. + optional string index = 2; + + // Request index (multiread only). + optional int32 request_index = 3; + + // Rows read. Each row is a struct with multiple fields, one for each column + // in read result. All rows have the same type. + repeated ValueList row = 4; + + // The type of rows read. It must be set if at least one row was read. + optional google.spanner.v1.StructType row_type = 5; +} + +// QueryResult contains result of a Query. +message QueryResult { + // Rows read. Each row is a struct with multiple fields, one for each column + // in read result. All rows have the same type. + repeated ValueList row = 1; + + // The type of rows read. It must be set if at least one row was read. + optional google.spanner.v1.StructType row_type = 2; +} + +// Raw ChangeStream records. +// Encodes one of: DataChangeRecord, HeartbeatRecord, ChildPartitionsRecord +// returned from the ChangeStream API. +message ChangeStreamRecord { + // Record represents one type of the change stream record. + oneof record { + // Data change record. + DataChangeRecord data_change = 1; + + // Child partitions record. + ChildPartitionsRecord child_partition = 2; + + // Heartbeat record. + HeartbeatRecord heartbeat = 3; + } +} + +// ChangeStream data change record. +message DataChangeRecord { + // Column types. + message ColumnType { + // Column name. + string name = 1; + + // Column type in JSON. + string type = 2; + + // Whether the column is a primary key column. + bool is_primary_key = 3; + + // The position of the column as defined in the schema. + int64 ordinal_position = 4; + } + + // Describes the changes that were made. + message Mod { + // The primary key values in JSON. + string keys = 1; + + // The new values of the changed columns in JSON. Only contain the non-key + // columns. + string new_values = 2; + + // The old values of the changed columns in JSON. Only contain the non-key + // columns. + string old_values = 3; + } + + // The timestamp in which the change was committed. + google.protobuf.Timestamp commit_time = 1; + + // The sequence number for the record within the transaction. + string record_sequence = 2; + + // A globally unique string that represents the transaction in which the + // change was committed. + string transaction_id = 3; + + // Indicates whether this is the last record for a transaction in the current + // partition. + bool is_last_record = 4; + + // Name of the table affected by the change. + string table = 5; + + // Column types defined in the schema. + repeated ColumnType column_types = 6; + + // Changes made in the transaction. + repeated Mod mods = 7; + + // Describes the type of change. One of INSERT, UPDATE or DELETE. + string mod_type = 8; + + // One of value capture type: NEW_VALUES, OLD_VALUES, OLD_AND_NEW_VALUES. + string value_capture_type = 9; + + // Number of records in transactions. + int64 record_count = 10; + + // Number of partitions in transactions. + int64 partition_count = 11; + + // Transaction tag info. + string transaction_tag = 12; + + // Whether the transaction is a system transactionn. + bool is_system_transaction = 13; +} + +// ChangeStream child partition record. +message ChildPartitionsRecord { + // A single child partition. + message ChildPartition { + // Partition token string used to identify the child partition in queries. + string token = 1; + + // Parent partition tokens of this child partition. + repeated string parent_partition_tokens = 2; + } + + // Data change records returned from child partitions in this child partitions + // record will have a commit timestamp greater than or equal to start_time. + google.protobuf.Timestamp start_time = 1; + + // A monotonically increasing sequence number that can be used to define the + // ordering of the child partitions record when there are multiple child + // partitions records returned with the same start_time in a particular + // partition. + string record_sequence = 2; + + // A set of child partitions and their associated information. + repeated ChildPartition child_partitions = 3; +} + +// ChangeStream heartbeat record. +message HeartbeatRecord { + // Timestamp for this heartbeat check. + google.protobuf.Timestamp heartbeat_time = 1; +} + +// Options for Cloud Spanner Service. +message SpannerOptions { + // Options for configuring the session pool + SessionPoolOptions session_pool_options = 1; +} + +// Options for the session pool used by the DatabaseClient. +message SessionPoolOptions { + // passing this as true, will make applicable RPCs use multiplexed sessions + // instead of regular sessions + bool use_multiplexed = 1; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor.yaml new file mode 100644 index 00000000..e96be6b9 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor.yaml @@ -0,0 +1,24 @@ +type: google.api.Service +config_version: 3 +name: spanner-cloud-executor.googleapis.com +title: Cloud Spanner Executor test API + +apis: +- name: google.spanner.executor.v1.SpannerExecutorProxy + +documentation: + summary: |- + Stop. This folder is likely not what you are looking for. + + This folder contains protocol buffer definitions for test-only API for + Cloud Spanner clients. Unless told otherwise by a Google Cloud + representative, do not use any of the contents of this folder. If you + would like to use Cloud Spanner, please consult our [official + documentation](https://cloud.google.com/spanner/docs/apis) for details on + our APIs, or else consider one of our + [client + libraries](https://cloud.google.com/spanner/docs/reference/libraries). + + This API defined in this folder is unreleased and may shut off, break, or + fail at any time for any users who are not registered as a part of a + private preview program. diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor_gapic.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor_gapic.yaml new file mode 100644 index 00000000..a5d1ebc5 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor_gapic.yaml @@ -0,0 +1,6 @@ +type: com.google.api.codegen.ConfigProto +config_schema_version: 2.0.0 +language_settings: + java: + package_name: com.google.cloud.spanner.executor.v1 + release_level: GA diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor_grpc_service_config.json b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor_grpc_service_config.json new file mode 100644 index 00000000..2257b454 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/executor/v1/spanner_cloud_executor_grpc_service_config.json @@ -0,0 +1,3 @@ +{ + "methodConfig": [] +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/BUILD.bazel b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/BUILD.bazel new file mode 100644 index 00000000..bbac4148 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/BUILD.bazel @@ -0,0 +1,325 @@ +# This file was automatically generated by BuildFileGenerator + +############################################################################## +# Common +############################################################################## +load("@rules_proto//proto:defs.bzl", "proto_library") +load( + "@com_google_googleapis_imports//:imports.bzl", + "cc_grpc_library", + "cc_proto_library", + "csharp_gapic_assembly_pkg", + "csharp_gapic_library", + "csharp_grpc_library", + "csharp_proto_library", + "go_gapic_assembly_pkg", + "go_gapic_library", + "go_proto_library", + "java_gapic_assembly_gradle_pkg", + "java_gapic_library", + "java_gapic_test", + "java_grpc_library", + "java_proto_library", + "nodejs_gapic_assembly_pkg", + "nodejs_gapic_library", + "php_gapic_assembly_pkg", + "php_gapic_library", + "php_proto_library", + "proto_library_with_info", + "py_gapic_assembly_pkg", + "py_gapic_library", + "py_test", + "ruby_cloud_gapic_library", + "ruby_gapic_assembly_pkg", + "ruby_grpc_library", + "ruby_proto_library", +) + +# This is an API workspace, having public visibility by default makes perfect sense. +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "spanner_proto", + srcs = [ + "change_stream.proto", + "commit_response.proto", + "keys.proto", + "mutation.proto", + "query_plan.proto", + "result_set.proto", + "spanner.proto", + "transaction.proto", + "type.proto", + ], + deps = [ + "//google/api:annotations_proto", + "//google/api:client_proto", + "//google/api:field_behavior_proto", + "//google/api:resource_proto", + "//google/rpc:status_proto", + "@com_google_protobuf//:duration_proto", + "@com_google_protobuf//:empty_proto", + "@com_google_protobuf//:struct_proto", + "@com_google_protobuf//:timestamp_proto", + ], +) + +proto_library_with_info( + name = "spanner_proto_with_info", + deps = [ + ":spanner_proto", + "//google/cloud:common_resources_proto", + ], +) + +java_proto_library( + name = "spanner_java_proto", + deps = [":spanner_proto"], +) + +java_grpc_library( + name = "spanner_java_grpc", + srcs = [":spanner_proto"], + deps = [":spanner_java_proto"], +) + +java_gapic_library( + name = "spanner_java_gapic", + srcs = [":spanner_proto_with_info"], + gapic_yaml = "spanner_gapic.yaml", + grpc_service_config = "spanner_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + test_deps = [ + ":spanner_java_grpc", + ], + transport = "grpc+rest", + deps = [ + ":spanner_java_proto", + ], +) + +java_gapic_test( + name = "spanner_java_gapic_test_suite", + test_classes = [ + "com.google.cloud.spanner.v1.SpannerClientHttpJsonTest", + "com.google.cloud.spanner.v1.SpannerClientTest", + ], + runtime_deps = [":spanner_java_gapic_test"], +) + +# Open Source Packages +java_gapic_assembly_gradle_pkg( + name = "google-cloud-spanner-v1-java", + include_samples = True, + transport = "grpc+rest", + deps = [ + ":spanner_java_gapic", + ":spanner_java_grpc", + ":spanner_java_proto", + ":spanner_proto", + ], +) + +go_proto_library( + name = "spanner_go_proto", + compilers = ["@io_bazel_rules_go//proto:go_grpc"], + importpath = "cloud.google.com/go/spanner/apiv1/spannerpb", + protos = [":spanner_proto"], + deps = [ + "//google/api:annotations_go_proto", + "//google/rpc:status_go_proto", + ], +) + +go_gapic_library( + name = "spanner_go_gapic", + srcs = [":spanner_proto_with_info"], + grpc_service_config = "spanner_grpc_service_config.json", + importpath = "cloud.google.com/go/spanner/apiv1;spanner", + release_level = "ga", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [ + ":spanner_go_proto", + "@io_bazel_rules_go//proto/wkt:duration_go_proto", + ], +) + +# Open Source Packages +go_gapic_assembly_pkg( + name = "gapi-cloud-spanner-v1-go", + deps = [ + ":spanner_go_gapic", + ":spanner_go_gapic_srcjar-snippets.srcjar", + ":spanner_go_gapic_srcjar-test.srcjar", + ":spanner_go_proto", + ], +) + +py_gapic_library( + name = "spanner_py_gapic", + srcs = [":spanner_proto"], + grpc_service_config = "spanner_grpc_service_config.json", + opt_args = ["python-gapic-namespace=google.cloud"], + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", +) + +py_test( + name = "spanner_py_gapic_test", + srcs = [ + "spanner_py_gapic_pytest.py", + "spanner_py_gapic_test.py", + ], + legacy_create_init = False, + deps = [":spanner_py_gapic"], +) + +py_gapic_assembly_pkg( + name = "spanner-v1-py", + deps = [ + ":spanner_py_gapic", + ], +) + +php_proto_library( + name = "spanner_php_proto", + deps = [":spanner_proto"], +) + +php_gapic_library( + name = "spanner_php_gapic", + srcs = [":spanner_proto_with_info"], + grpc_service_config = "spanner_grpc_service_config.json", + migration_mode = "MIGRATING", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [":spanner_php_proto"], +) + +# Open Source Packages +php_gapic_assembly_pkg( + name = "google-cloud-spanner-v1-php", + deps = [ + ":spanner_php_gapic", + ":spanner_php_proto", + ], +) + +nodejs_gapic_library( + name = "spanner_nodejs_gapic", + package_name = "@google-cloud/spanner", + src = ":spanner_proto_with_info", + extra_protoc_parameters = [ + "metadata", + "template=typescript_gapic", + ], + grpc_service_config = "spanner_grpc_service_config.json", + handwritten_layer = True, + main_service = "spanner", + package = "google.spanner.v1", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + transport = "grpc+rest", + deps = [], +) + +nodejs_gapic_assembly_pkg( + name = "spanner-v1-nodejs", + deps = [ + ":spanner_nodejs_gapic", + ":spanner_proto", + ], +) + +ruby_proto_library( + name = "spanner_ruby_proto", + deps = [":spanner_proto"], +) + +ruby_grpc_library( + name = "spanner_ruby_grpc", + srcs = [":spanner_proto"], + deps = [":spanner_ruby_proto"], +) + +ruby_cloud_gapic_library( + name = "spanner_ruby_gapic", + srcs = [":spanner_proto_with_info"], + extra_protoc_parameters = [ + "ruby-cloud-gem-name=google-cloud-spanner-v1", + "ruby-cloud-env-prefix=SPANNER", + "ruby-cloud-product-url=https://cloud.google.com/spanner", + "ruby-cloud-api-id=spanner.googleapis.com", + "ruby-cloud-api-shortname=spanner", + ], + grpc_service_config = "spanner_grpc_service_config.json", + rest_numeric_enums = True, + ruby_cloud_description = "Cloud Spanner is a managed, mission-critical, globally consistent and scalable relational database service.", + ruby_cloud_title = "Cloud Spanner V1", + service_yaml = "spanner.yaml", + deps = [ + ":spanner_ruby_grpc", + ":spanner_ruby_proto", + ], +) + +# Open Source Packages +ruby_gapic_assembly_pkg( + name = "google-cloud-spanner-v1-ruby", + deps = [ + ":spanner_ruby_gapic", + ":spanner_ruby_grpc", + ":spanner_ruby_proto", + ], +) + +csharp_proto_library( + name = "spanner_csharp_proto", + deps = [":spanner_proto"], +) + +csharp_grpc_library( + name = "spanner_csharp_grpc", + srcs = [":spanner_proto"], + deps = [":spanner_csharp_proto"], +) + +csharp_gapic_library( + name = "spanner_csharp_gapic", + srcs = [":spanner_proto_with_info"], + common_resources_config = "@gax_dotnet//:Google.Api.Gax/ResourceNames/CommonResourcesConfig.json", + grpc_service_config = "spanner_grpc_service_config.json", + rest_numeric_enums = True, + service_yaml = "spanner.yaml", + deps = [ + ":spanner_csharp_grpc", + ":spanner_csharp_proto", + ], +) + +# Open Source Packages +csharp_gapic_assembly_pkg( + name = "google-cloud-spanner-v1-csharp", + deps = [ + ":spanner_csharp_gapic", + ":spanner_csharp_grpc", + ":spanner_csharp_proto", + ], +) + +cc_proto_library( + name = "spanner_cc_proto", + deps = [":spanner_proto"], +) + +cc_grpc_library( + name = "spanner_cc_grpc", + srcs = [":spanner_proto"], + grpc_only = True, + deps = [":spanner_cc_proto"], +) diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/change_stream.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/change_stream.proto new file mode 100644 index 00000000..53c71078 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/change_stream.proto @@ -0,0 +1,451 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/spanner/v1/type.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "ChangeStreamProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// Spanner Change Streams enable customers to capture and stream out changes to +// their Spanner databases in real-time. A change stream +// can be created with option partition_mode='IMMUTABLE_KEY_RANGE' or +// partition_mode='MUTABLE_KEY_RANGE'. +// +// This message is only used in Change Streams created with the option +// partition_mode='MUTABLE_KEY_RANGE'. Spanner automatically creates a special +// Table-Valued Function (TVF) along with each Change Streams. The function +// provides access to the change stream's records. The function is named +// READ_ (where is the +// name of the change stream), and it returns a table with only one column +// called ChangeRecord. +message ChangeStreamRecord { + // A data change record contains a set of changes to a table with the same + // modification type (insert, update, or delete) committed at the same commit + // timestamp in one change stream partition for the same transaction. Multiple + // data change records can be returned for the same transaction across + // multiple change stream partitions. + message DataChangeRecord { + // Metadata for a column. + message ColumnMetadata { + // Name of the column. + string name = 1; + + // Type of the column. + Type type = 2; + + // Indicates whether the column is a primary key column. + bool is_primary_key = 3; + + // Ordinal position of the column based on the original table definition + // in the schema starting with a value of 1. + int64 ordinal_position = 4; + } + + // Returns the value and associated metadata for a particular field of the + // [Mod][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.Mod]. + message ModValue { + // Index within the repeated + // [column_metadata][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.column_metadata] + // field, to obtain the column metadata for the column that was modified. + int32 column_metadata_index = 1; + + // The value of the column. + google.protobuf.Value value = 2; + } + + // A mod describes all data changes in a watched table row. + message Mod { + // Returns the value of the primary key of the modified row. + repeated ModValue keys = 1; + + // Returns the old values before the change for the modified columns. + // Always empty for + // [INSERT][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.ModType.INSERT], + // or if old values are not being captured specified by + // [value_capture_type][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.ValueCaptureType]. + repeated ModValue old_values = 2; + + // Returns the new values after the change for the modified columns. + // Always empty for + // [DELETE][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.ModType.DELETE]. + repeated ModValue new_values = 3; + } + + // Mod type describes the type of change Spanner applied to the data. For + // example, if the client submits an INSERT_OR_UPDATE request, Spanner will + // perform an insert if there is no existing row and return ModType INSERT. + // Alternatively, if there is an existing row, Spanner will perform an + // update and return ModType UPDATE. + enum ModType { + // Not specified. + MOD_TYPE_UNSPECIFIED = 0; + + // Indicates data was inserted. + INSERT = 10; + + // Indicates existing data was updated. + UPDATE = 20; + + // Indicates existing data was deleted. + DELETE = 30; + } + + // Value capture type describes which values are recorded in the data + // change record. + enum ValueCaptureType { + // Not specified. + VALUE_CAPTURE_TYPE_UNSPECIFIED = 0; + + // Records both old and new values of the modified watched columns. + OLD_AND_NEW_VALUES = 10; + + // Records only new values of the modified watched columns. + NEW_VALUES = 20; + + // Records new values of all watched columns, including modified and + // unmodified columns. + NEW_ROW = 30; + + // Records the new values of all watched columns, including modified and + // unmodified columns. Also records the old values of the modified + // columns. + NEW_ROW_AND_OLD_VALUES = 40; + } + + // Indicates the timestamp in which the change was committed. + // DataChangeRecord.commit_timestamps, + // PartitionStartRecord.start_timestamps, + // PartitionEventRecord.commit_timestamps, and + // PartitionEndRecord.end_timestamps can have the same value in the same + // partition. + google.protobuf.Timestamp commit_timestamp = 1; + + // Record sequence numbers are unique and monotonically increasing (but not + // necessarily contiguous) for a specific timestamp across record + // types in the same partition. To guarantee ordered processing, the reader + // should process records (of potentially different types) in + // record_sequence order for a specific timestamp in the same partition. + // + // The record sequence number ordering across partitions is only meaningful + // in the context of a specific transaction. Record sequence numbers are + // unique across partitions for a specific transaction. Sort the + // DataChangeRecords for the same + // [server_transaction_id][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.server_transaction_id] + // by + // [record_sequence][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.record_sequence] + // to reconstruct the ordering of the changes within the transaction. + string record_sequence = 2; + + // Provides a globally unique string that represents the transaction in + // which the change was committed. Multiple transactions can have the same + // commit timestamp, but each transaction has a unique + // server_transaction_id. + string server_transaction_id = 3; + + // Indicates whether this is the last record for a transaction in the + // current partition. Clients can use this field to determine when all + // records for a transaction in the current partition have been received. + bool is_last_record_in_transaction_in_partition = 4; + + // Name of the table affected by the change. + string table = 5; + + // Provides metadata describing the columns associated with the + // [mods][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.mods] listed + // below. + repeated ColumnMetadata column_metadata = 6; + + // Describes the changes that were made. + repeated Mod mods = 7; + + // Describes the type of change. + ModType mod_type = 8; + + // Describes the value capture type that was specified in the change stream + // configuration when this change was captured. + ValueCaptureType value_capture_type = 9; + + // Indicates the number of data change records that are part of this + // transaction across all change stream partitions. This value can be used + // to assemble all the records associated with a particular transaction. + int32 number_of_records_in_transaction = 10; + + // Indicates the number of partitions that return data change records for + // this transaction. This value can be helpful in assembling all records + // associated with a particular transaction. + int32 number_of_partitions_in_transaction = 11; + + // Indicates the transaction tag associated with this transaction. + string transaction_tag = 12; + + // Indicates whether the transaction is a system transaction. System + // transactions include those issued by time-to-live (TTL), column backfill, + // etc. + bool is_system_transaction = 13; + } + + // A heartbeat record is returned as a progress indicator, when there are no + // data changes or any other partition record types in the change stream + // partition. + message HeartbeatRecord { + // Indicates the timestamp at which the query has returned all the records + // in the change stream partition with timestamp <= heartbeat timestamp. + // The heartbeat timestamp will not be the same as the timestamps of other + // record types in the same partition. + google.protobuf.Timestamp timestamp = 1; + } + + // A partition start record serves as a notification that the client should + // schedule the partitions to be queried. PartitionStartRecord returns + // information about one or more partitions. + message PartitionStartRecord { + // Start timestamp at which the partitions should be queried to return + // change stream records with timestamps >= start_timestamp. + // DataChangeRecord.commit_timestamps, + // PartitionStartRecord.start_timestamps, + // PartitionEventRecord.commit_timestamps, and + // PartitionEndRecord.end_timestamps can have the same value in the same + // partition. + google.protobuf.Timestamp start_timestamp = 1; + + // Record sequence numbers are unique and monotonically increasing (but not + // necessarily contiguous) for a specific timestamp across record + // types in the same partition. To guarantee ordered processing, the reader + // should process records (of potentially different types) in + // record_sequence order for a specific timestamp in the same partition. + string record_sequence = 2; + + // Unique partition identifiers to be used in queries. + repeated string partition_tokens = 3; + } + + // A partition end record serves as a notification that the client should stop + // reading the partition. No further records are expected to be retrieved on + // it. + message PartitionEndRecord { + // End timestamp at which the change stream partition is terminated. All + // changes generated by this partition will have timestamps <= + // end_timestamp. DataChangeRecord.commit_timestamps, + // PartitionStartRecord.start_timestamps, + // PartitionEventRecord.commit_timestamps, and + // PartitionEndRecord.end_timestamps can have the same value in the same + // partition. PartitionEndRecord is the last record returned for a + // partition. + google.protobuf.Timestamp end_timestamp = 1; + + // Record sequence numbers are unique and monotonically increasing (but not + // necessarily contiguous) for a specific timestamp across record + // types in the same partition. To guarantee ordered processing, the reader + // should process records (of potentially different types) in + // record_sequence order for a specific timestamp in the same partition. + string record_sequence = 2; + + // Unique partition identifier describing the terminated change stream + // partition. + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEndRecord.partition_token] + // is equal to the partition token of the change stream partition currently + // queried to return this PartitionEndRecord. + string partition_token = 3; + } + + // A partition event record describes key range changes for a change stream + // partition. The changes to a row defined by its primary key can be captured + // in one change stream partition for a specific time range, and then be + // captured in a different change stream partition for a different time range. + // This movement of key ranges across change stream partitions is a reflection + // of activities, such as Spanner's dynamic splitting and load balancing, etc. + // Processing this event is needed if users want to guarantee processing of + // the changes for any key in timestamp order. If time ordered processing of + // changes for a primary key is not needed, this event can be ignored. + // To guarantee time ordered processing for each primary key, if the event + // describes move-ins, the reader of this partition needs to wait until the + // readers of the source partitions have processed all records with timestamps + // <= this PartitionEventRecord.commit_timestamp, before advancing beyond this + // PartitionEventRecord. If the event describes move-outs, the reader can + // notify the readers of the destination partitions that they can continue + // processing. + message PartitionEventRecord { + // Describes move-in of the key ranges into the change stream partition + // identified by + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + // + // To maintain processing the changes for a particular key in timestamp + // order, the query processing the change stream partition identified by + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token] + // should not advance beyond the partition event record commit timestamp + // until the queries processing the source change stream partitions have + // processed all change stream records with timestamps <= the partition + // event record commit timestamp. + message MoveInEvent { + // An unique partition identifier describing the source change stream + // partition that recorded changes for the key range that is moving + // into this partition. + string source_partition_token = 1; + } + + // Describes move-out of the key ranges out of the change stream partition + // identified by + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + // + // To maintain processing the changes for a particular key in timestamp + // order, the query processing the + // [MoveOutEvent][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.MoveOutEvent] + // in the partition identified by + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token] + // should inform the queries processing the destination partitions that + // they can unblock and proceed processing records past the + // [commit_timestamp][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.commit_timestamp]. + message MoveOutEvent { + // An unique partition identifier describing the destination change + // stream partition that will record changes for the key range that is + // moving out of this partition. + string destination_partition_token = 1; + } + + // Indicates the commit timestamp at which the key range change occurred. + // DataChangeRecord.commit_timestamps, + // PartitionStartRecord.start_timestamps, + // PartitionEventRecord.commit_timestamps, and + // PartitionEndRecord.end_timestamps can have the same value in the same + // partition. + google.protobuf.Timestamp commit_timestamp = 1; + + // Record sequence numbers are unique and monotonically increasing (but not + // necessarily contiguous) for a specific timestamp across record + // types in the same partition. To guarantee ordered processing, the reader + // should process records (of potentially different types) in + // record_sequence order for a specific timestamp in the same partition. + string record_sequence = 2; + + // Unique partition identifier describing the partition this event + // occurred on. + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token] + // is equal to the partition token of the change stream partition currently + // queried to return this PartitionEventRecord. + string partition_token = 3; + + // Set when one or more key ranges are moved into the change stream + // partition identified by + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + // + // Example: Two key ranges are moved into partition (P1) from partition (P2) + // and partition (P3) in a single transaction at timestamp T. + // + // The PartitionEventRecord returned in P1 will reflect the move as: + // + // PartitionEventRecord { + // commit_timestamp: T + // partition_token: "P1" + // move_in_events { + // source_partition_token: "P2" + // } + // move_in_events { + // source_partition_token: "P3" + // } + // } + // + // The PartitionEventRecord returned in P2 will reflect the move as: + // + // PartitionEventRecord { + // commit_timestamp: T + // partition_token: "P2" + // move_out_events { + // destination_partition_token: "P1" + // } + // } + // + // The PartitionEventRecord returned in P3 will reflect the move as: + // + // PartitionEventRecord { + // commit_timestamp: T + // partition_token: "P3" + // move_out_events { + // destination_partition_token: "P1" + // } + // } + repeated MoveInEvent move_in_events = 4; + + // Set when one or more key ranges are moved out of the change stream + // partition identified by + // [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + // + // Example: Two key ranges are moved out of partition (P1) to partition (P2) + // and partition (P3) in a single transaction at timestamp T. + // + // The PartitionEventRecord returned in P1 will reflect the move as: + // + // PartitionEventRecord { + // commit_timestamp: T + // partition_token: "P1" + // move_out_events { + // destination_partition_token: "P2" + // } + // move_out_events { + // destination_partition_token: "P3" + // } + // } + // + // The PartitionEventRecord returned in P2 will reflect the move as: + // + // PartitionEventRecord { + // commit_timestamp: T + // partition_token: "P2" + // move_in_events { + // source_partition_token: "P1" + // } + // } + // + // The PartitionEventRecord returned in P3 will reflect the move as: + // + // PartitionEventRecord { + // commit_timestamp: T + // partition_token: "P3" + // move_in_events { + // source_partition_token: "P1" + // } + // } + repeated MoveOutEvent move_out_events = 5; + } + + // One of the change stream subrecords. + oneof record { + // Data change record describing a data change for a change stream + // partition. + DataChangeRecord data_change_record = 1; + + // Heartbeat record describing a heartbeat for a change stream partition. + HeartbeatRecord heartbeat_record = 2; + + // Partition start record describing a new change stream partition. + PartitionStartRecord partition_start_record = 3; + + // Partition end record describing a terminated change stream partition. + PartitionEndRecord partition_end_record = 4; + + // Partition event record describing key range changes for a change stream + // partition. + PartitionEventRecord partition_event_record = 5; + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/commit_response.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/commit_response.proto new file mode 100644 index 00000000..beeb3123 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/commit_response.proto @@ -0,0 +1,59 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/protobuf/timestamp.proto"; +import "google/spanner/v1/transaction.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "CommitResponseProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// The response for [Commit][google.spanner.v1.Spanner.Commit]. +message CommitResponse { + // Additional statistics about a commit. + message CommitStats { + // The total number of mutations for the transaction. Knowing the + // `mutation_count` value can help you maximize the number of mutations + // in a transaction and minimize the number of API round trips. You can + // also monitor this value to prevent transactions from exceeding the system + // [limit](https://cloud.google.com/spanner/quotas#limits_for_creating_reading_updating_and_deleting_data). + // If the number of mutations exceeds the limit, the server returns + // [INVALID_ARGUMENT](https://cloud.google.com/spanner/docs/reference/rest/v1/Code#ENUM_VALUES.INVALID_ARGUMENT). + int64 mutation_count = 1; + } + + // The Cloud Spanner timestamp at which the transaction committed. + google.protobuf.Timestamp commit_timestamp = 1; + + // The statistics about this Commit. Not returned by default. + // For more information, see + // [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats]. + CommitStats commit_stats = 2; + + // Clients should examine and retry the commit if any of the following + // reasons are populated. + oneof MultiplexedSessionRetry { + // If specified, transaction has not committed yet. + // Clients must retry the commit with the new precommit token. + MultiplexedSessionPrecommitToken precommit_token = 4; + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/keys.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/keys.proto new file mode 100644 index 00000000..9eadda47 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/keys.proto @@ -0,0 +1,163 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/protobuf/struct.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "KeysProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// KeyRange represents a range of rows in a table or index. +// +// A range has a start key and an end key. These keys can be open or +// closed, indicating if the range includes rows with that key. +// +// Keys are represented by lists, where the ith value in the list +// corresponds to the ith component of the table or index primary key. +// Individual values are encoded as described +// [here][google.spanner.v1.TypeCode]. +// +// For example, consider the following table definition: +// +// CREATE TABLE UserEvents ( +// UserName STRING(MAX), +// EventDate STRING(10) +// ) PRIMARY KEY(UserName, EventDate); +// +// The following keys name rows in this table: +// +// ["Bob", "2014-09-23"] +// ["Alfred", "2015-06-12"] +// +// Since the `UserEvents` table's `PRIMARY KEY` clause names two +// columns, each `UserEvents` key has two elements; the first is the +// `UserName`, and the second is the `EventDate`. +// +// Key ranges with multiple components are interpreted +// lexicographically by component using the table or index key's declared +// sort order. For example, the following range returns all events for +// user `"Bob"` that occurred in the year 2015: +// +// "start_closed": ["Bob", "2015-01-01"] +// "end_closed": ["Bob", "2015-12-31"] +// +// Start and end keys can omit trailing key components. This affects the +// inclusion and exclusion of rows that exactly match the provided key +// components: if the key is closed, then rows that exactly match the +// provided components are included; if the key is open, then rows +// that exactly match are not included. +// +// For example, the following range includes all events for `"Bob"` that +// occurred during and after the year 2000: +// +// "start_closed": ["Bob", "2000-01-01"] +// "end_closed": ["Bob"] +// +// The next example retrieves all events for `"Bob"`: +// +// "start_closed": ["Bob"] +// "end_closed": ["Bob"] +// +// To retrieve events before the year 2000: +// +// "start_closed": ["Bob"] +// "end_open": ["Bob", "2000-01-01"] +// +// The following range includes all rows in the table: +// +// "start_closed": [] +// "end_closed": [] +// +// This range returns all users whose `UserName` begins with any +// character from A to C: +// +// "start_closed": ["A"] +// "end_open": ["D"] +// +// This range returns all users whose `UserName` begins with B: +// +// "start_closed": ["B"] +// "end_open": ["C"] +// +// Key ranges honor column sort order. For example, suppose a table is +// defined as follows: +// +// CREATE TABLE DescendingSortedTable { +// Key INT64, +// ... +// ) PRIMARY KEY(Key DESC); +// +// The following range retrieves all rows with key values between 1 +// and 100 inclusive: +// +// "start_closed": ["100"] +// "end_closed": ["1"] +// +// Note that 100 is passed as the start, and 1 is passed as the end, +// because `Key` is a descending column in the schema. +message KeyRange { + // The start key must be provided. It can be either closed or open. + oneof start_key_type { + // If the start is closed, then the range includes all rows whose + // first `len(start_closed)` key columns exactly match `start_closed`. + google.protobuf.ListValue start_closed = 1; + + // If the start is open, then the range excludes rows whose first + // `len(start_open)` key columns exactly match `start_open`. + google.protobuf.ListValue start_open = 2; + } + + // The end key must be provided. It can be either closed or open. + oneof end_key_type { + // If the end is closed, then the range includes all rows whose + // first `len(end_closed)` key columns exactly match `end_closed`. + google.protobuf.ListValue end_closed = 3; + + // If the end is open, then the range excludes rows whose first + // `len(end_open)` key columns exactly match `end_open`. + google.protobuf.ListValue end_open = 4; + } +} + +// `KeySet` defines a collection of Cloud Spanner keys and/or key ranges. All +// the keys are expected to be in the same table or index. The keys need +// not be sorted in any particular way. +// +// If the same key is specified multiple times in the set (for example +// if two ranges, two keys, or a key and a range overlap), Cloud Spanner +// behaves as if the key were only specified once. +message KeySet { + // A list of specific keys. Entries in `keys` should have exactly as + // many elements as there are columns in the primary or index key + // with which this `KeySet` is used. Individual key values are + // encoded as described [here][google.spanner.v1.TypeCode]. + repeated google.protobuf.ListValue keys = 1; + + // A list of key ranges. See [KeyRange][google.spanner.v1.KeyRange] for more information about + // key range specifications. + repeated KeyRange ranges = 2; + + // For convenience `all` can be set to `true` to indicate that this + // `KeySet` matches all keys in the table or index. Note that any keys + // specified in `keys` or `ranges` are only yielded once. + bool all = 3; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/mutation.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/mutation.proto new file mode 100644 index 00000000..c8af1af8 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/mutation.proto @@ -0,0 +1,107 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/api/field_behavior.proto"; +import "google/protobuf/struct.proto"; +import "google/spanner/v1/keys.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "MutationProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// A modification to one or more Cloud Spanner rows. Mutations can be +// applied to a Cloud Spanner database by sending them in a +// [Commit][google.spanner.v1.Spanner.Commit] call. +message Mutation { + // Arguments to [insert][google.spanner.v1.Mutation.insert], [update][google.spanner.v1.Mutation.update], [insert_or_update][google.spanner.v1.Mutation.insert_or_update], and + // [replace][google.spanner.v1.Mutation.replace] operations. + message Write { + // Required. The table whose rows will be written. + string table = 1 [(google.api.field_behavior) = REQUIRED]; + + // The names of the columns in [table][google.spanner.v1.Mutation.Write.table] to be written. + // + // The list of columns must contain enough columns to allow + // Cloud Spanner to derive values for all primary key columns in the + // row(s) to be modified. + repeated string columns = 2; + + // The values to be written. `values` can contain more than one + // list of values. If it does, then multiple rows are written, one + // for each entry in `values`. Each list in `values` must have + // exactly as many entries as there are entries in [columns][google.spanner.v1.Mutation.Write.columns] + // above. Sending multiple lists is equivalent to sending multiple + // `Mutation`s, each containing one `values` entry and repeating + // [table][google.spanner.v1.Mutation.Write.table] and [columns][google.spanner.v1.Mutation.Write.columns]. Individual values in each list are + // encoded as described [here][google.spanner.v1.TypeCode]. + repeated google.protobuf.ListValue values = 3; + } + + // Arguments to [delete][google.spanner.v1.Mutation.delete] operations. + message Delete { + // Required. The table whose rows will be deleted. + string table = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The primary keys of the rows within [table][google.spanner.v1.Mutation.Delete.table] to delete. The + // primary keys must be specified in the order in which they appear in the + // `PRIMARY KEY()` clause of the table's equivalent DDL statement (the DDL + // statement used to create the table). + // Delete is idempotent. The transaction will succeed even if some or all + // rows do not exist. + KeySet key_set = 2 [(google.api.field_behavior) = REQUIRED]; + } + + // Required. The operation to perform. + oneof operation { + // Insert new rows in a table. If any of the rows already exist, + // the write or transaction fails with error `ALREADY_EXISTS`. + Write insert = 1; + + // Update existing rows in a table. If any of the rows does not + // already exist, the transaction fails with error `NOT_FOUND`. + Write update = 2; + + // Like [insert][google.spanner.v1.Mutation.insert], except that if the row already exists, then + // its column values are overwritten with the ones provided. Any + // column values not explicitly written are preserved. + // + // When using [insert_or_update][google.spanner.v1.Mutation.insert_or_update], just as when using [insert][google.spanner.v1.Mutation.insert], all `NOT + // NULL` columns in the table must be given a value. This holds true + // even when the row already exists and will therefore actually be updated. + Write insert_or_update = 3; + + // Like [insert][google.spanner.v1.Mutation.insert], except that if the row already exists, it is + // deleted, and the column values provided are inserted + // instead. Unlike [insert_or_update][google.spanner.v1.Mutation.insert_or_update], this means any values not + // explicitly written become `NULL`. + // + // In an interleaved table, if you create the child table with the + // `ON DELETE CASCADE` annotation, then replacing a parent row + // also deletes the child rows. Otherwise, you must delete the + // child rows before you replace the parent row. + Write replace = 4; + + // Delete rows from a table. Succeeds whether or not the named + // rows were present. + Delete delete = 5; + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/query_plan.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/query_plan.proto new file mode 100644 index 00000000..10482845 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/query_plan.proto @@ -0,0 +1,128 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/protobuf/struct.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "QueryPlanProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// Node information for nodes appearing in a [QueryPlan.plan_nodes][google.spanner.v1.QueryPlan.plan_nodes]. +message PlanNode { + // The kind of [PlanNode][google.spanner.v1.PlanNode]. Distinguishes between the two different kinds of + // nodes that can appear in a query plan. + enum Kind { + // Not specified. + KIND_UNSPECIFIED = 0; + + // Denotes a Relational operator node in the expression tree. Relational + // operators represent iterative processing of rows during query execution. + // For example, a `TableScan` operation that reads rows from a table. + RELATIONAL = 1; + + // Denotes a Scalar node in the expression tree. Scalar nodes represent + // non-iterable entities in the query plan. For example, constants or + // arithmetic operators appearing inside predicate expressions or references + // to column names. + SCALAR = 2; + } + + // Metadata associated with a parent-child relationship appearing in a + // [PlanNode][google.spanner.v1.PlanNode]. + message ChildLink { + // The node to which the link points. + int32 child_index = 1; + + // The type of the link. For example, in Hash Joins this could be used to + // distinguish between the build child and the probe child, or in the case + // of the child being an output variable, to represent the tag associated + // with the output variable. + string type = 2; + + // Only present if the child node is [SCALAR][google.spanner.v1.PlanNode.Kind.SCALAR] and corresponds + // to an output variable of the parent node. The field carries the name of + // the output variable. + // For example, a `TableScan` operator that reads rows from a table will + // have child links to the `SCALAR` nodes representing the output variables + // created for each column that is read by the operator. The corresponding + // `variable` fields will be set to the variable names assigned to the + // columns. + string variable = 3; + } + + // Condensed representation of a node and its subtree. Only present for + // `SCALAR` [PlanNode(s)][google.spanner.v1.PlanNode]. + message ShortRepresentation { + // A string representation of the expression subtree rooted at this node. + string description = 1; + + // A mapping of (subquery variable name) -> (subquery node id) for cases + // where the `description` string of this node references a `SCALAR` + // subquery contained in the expression subtree rooted at this node. The + // referenced `SCALAR` subquery may not necessarily be a direct child of + // this node. + map subqueries = 2; + } + + // The `PlanNode`'s index in [node list][google.spanner.v1.QueryPlan.plan_nodes]. + int32 index = 1; + + // Used to determine the type of node. May be needed for visualizing + // different kinds of nodes differently. For example, If the node is a + // [SCALAR][google.spanner.v1.PlanNode.Kind.SCALAR] node, it will have a condensed representation + // which can be used to directly embed a description of the node in its + // parent. + Kind kind = 2; + + // The display name for the node. + string display_name = 3; + + // List of child node `index`es and their relationship to this parent. + repeated ChildLink child_links = 4; + + // Condensed representation for [SCALAR][google.spanner.v1.PlanNode.Kind.SCALAR] nodes. + ShortRepresentation short_representation = 5; + + // Attributes relevant to the node contained in a group of key-value pairs. + // For example, a Parameter Reference node could have the following + // information in its metadata: + // + // { + // "parameter_reference": "param1", + // "parameter_type": "array" + // } + google.protobuf.Struct metadata = 6; + + // The execution statistics associated with the node, contained in a group of + // key-value pairs. Only present if the plan was returned as a result of a + // profile query. For example, number of executions, number of rows/time per + // execution etc. + google.protobuf.Struct execution_stats = 7; +} + +// Contains an ordered list of nodes appearing in the query plan. +message QueryPlan { + // The nodes in the query plan. Plan nodes are returned in pre-order starting + // with the plan root. Each [PlanNode][google.spanner.v1.PlanNode]'s `id` corresponds to its index in + // `plan_nodes`. + repeated PlanNode plan_nodes = 1; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/result_set.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/result_set.proto new file mode 100644 index 00000000..c80bff2a --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/result_set.proto @@ -0,0 +1,243 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/api/field_behavior.proto"; +import "google/protobuf/struct.proto"; +import "google/spanner/v1/query_plan.proto"; +import "google/spanner/v1/transaction.proto"; +import "google/spanner/v1/type.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "ResultSetProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// Results from [Read][google.spanner.v1.Spanner.Read] or +// [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql]. +message ResultSet { + // Metadata about the result set, such as row type information. + ResultSetMetadata metadata = 1; + + // Each element in `rows` is a row whose format is defined by + // [metadata.row_type][google.spanner.v1.ResultSetMetadata.row_type]. The ith + // element in each row matches the ith field in + // [metadata.row_type][google.spanner.v1.ResultSetMetadata.row_type]. Elements + // are encoded based on type as described [here][google.spanner.v1.TypeCode]. + repeated google.protobuf.ListValue rows = 2; + + // Query plan and execution statistics for the SQL statement that + // produced this result set. These can be requested by setting + // [ExecuteSqlRequest.query_mode][google.spanner.v1.ExecuteSqlRequest.query_mode]. + // DML statements always produce stats containing the number of rows + // modified, unless executed using the + // [ExecuteSqlRequest.QueryMode.PLAN][google.spanner.v1.ExecuteSqlRequest.QueryMode.PLAN] + // [ExecuteSqlRequest.query_mode][google.spanner.v1.ExecuteSqlRequest.query_mode]. + // Other fields might or might not be populated, based on the + // [ExecuteSqlRequest.query_mode][google.spanner.v1.ExecuteSqlRequest.query_mode]. + ResultSetStats stats = 3; + + // Optional. A precommit token is included if the read-write transaction is on + // a multiplexed session. Pass the precommit token with the highest sequence + // number from this transaction attempt to the + // [Commit][google.spanner.v1.Spanner.Commit] request for this transaction. + MultiplexedSessionPrecommitToken precommit_token = 5 + [(google.api.field_behavior) = OPTIONAL]; +} + +// Partial results from a streaming read or SQL query. Streaming reads and +// SQL queries better tolerate large result sets, large rows, and large +// values, but are a little trickier to consume. +message PartialResultSet { + // Metadata about the result set, such as row type information. + // Only present in the first response. + ResultSetMetadata metadata = 1; + + // A streamed result set consists of a stream of values, which might + // be split into many `PartialResultSet` messages to accommodate + // large rows and/or large values. Every N complete values defines a + // row, where N is equal to the number of entries in + // [metadata.row_type.fields][google.spanner.v1.StructType.fields]. + // + // Most values are encoded based on type as described + // [here][google.spanner.v1.TypeCode]. + // + // It's possible that the last value in values is "chunked", + // meaning that the rest of the value is sent in subsequent + // `PartialResultSet`(s). This is denoted by the + // [chunked_value][google.spanner.v1.PartialResultSet.chunked_value] field. + // Two or more chunked values can be merged to form a complete value as + // follows: + // + // * `bool/number/null`: can't be chunked + // * `string`: concatenate the strings + // * `list`: concatenate the lists. If the last element in a list is a + // `string`, `list`, or `object`, merge it with the first element in + // the next list by applying these rules recursively. + // * `object`: concatenate the (field name, field value) pairs. If a + // field name is duplicated, then apply these rules recursively + // to merge the field values. + // + // Some examples of merging: + // + // Strings are concatenated. + // "foo", "bar" => "foobar" + // + // Lists of non-strings are concatenated. + // [2, 3], [4] => [2, 3, 4] + // + // Lists are concatenated, but the last and first elements are merged + // because they are strings. + // ["a", "b"], ["c", "d"] => ["a", "bc", "d"] + // + // Lists are concatenated, but the last and first elements are merged + // because they are lists. Recursively, the last and first elements + // of the inner lists are merged because they are strings. + // ["a", ["b", "c"]], [["d"], "e"] => ["a", ["b", "cd"], "e"] + // + // Non-overlapping object fields are combined. + // {"a": "1"}, {"b": "2"} => {"a": "1", "b": 2"} + // + // Overlapping object fields are merged. + // {"a": "1"}, {"a": "2"} => {"a": "12"} + // + // Examples of merging objects containing lists of strings. + // {"a": ["1"]}, {"a": ["2"]} => {"a": ["12"]} + // + // For a more complete example, suppose a streaming SQL query is + // yielding a result set whose rows contain a single string + // field. The following `PartialResultSet`s might be yielded: + // + // { + // "metadata": { ... } + // "values": ["Hello", "W"] + // "chunked_value": true + // "resume_token": "Af65..." + // } + // { + // "values": ["orl"] + // "chunked_value": true + // } + // { + // "values": ["d"] + // "resume_token": "Zx1B..." + // } + // + // This sequence of `PartialResultSet`s encodes two rows, one + // containing the field value `"Hello"`, and a second containing the + // field value `"World" = "W" + "orl" + "d"`. + // + // Not all `PartialResultSet`s contain a `resume_token`. Execution can only be + // resumed from a previously yielded `resume_token`. For the above sequence of + // `PartialResultSet`s, resuming the query with `"resume_token": "Af65..."` + // yields results from the `PartialResultSet` with value "orl". + repeated google.protobuf.Value values = 2; + + // If true, then the final value in + // [values][google.spanner.v1.PartialResultSet.values] is chunked, and must be + // combined with more values from subsequent `PartialResultSet`s to obtain a + // complete field value. + bool chunked_value = 3; + + // Streaming calls might be interrupted for a variety of reasons, such + // as TCP connection loss. If this occurs, the stream of results can + // be resumed by re-sending the original request and including + // `resume_token`. Note that executing any other transaction in the + // same session invalidates the token. + bytes resume_token = 4; + + // Query plan and execution statistics for the statement that produced this + // streaming result set. These can be requested by setting + // [ExecuteSqlRequest.query_mode][google.spanner.v1.ExecuteSqlRequest.query_mode] + // and are sent only once with the last response in the stream. This field is + // also present in the last response for DML statements. + ResultSetStats stats = 5; + + // Optional. A precommit token is included if the read-write transaction + // has multiplexed sessions enabled. Pass the precommit token with the highest + // sequence number from this transaction attempt to the + // [Commit][google.spanner.v1.Spanner.Commit] request for this transaction. + MultiplexedSessionPrecommitToken precommit_token = 8 + [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Indicates whether this is the last `PartialResultSet` in the + // stream. The server might optionally set this field. Clients shouldn't rely + // on this field being set in all cases. + bool last = 9 [(google.api.field_behavior) = OPTIONAL]; +} + +// Metadata about a [ResultSet][google.spanner.v1.ResultSet] or +// [PartialResultSet][google.spanner.v1.PartialResultSet]. +message ResultSetMetadata { + // Indicates the field names and types for the rows in the result + // set. For example, a SQL query like `"SELECT UserId, UserName FROM + // Users"` could return a `row_type` value like: + // + // "fields": [ + // { "name": "UserId", "type": { "code": "INT64" } }, + // { "name": "UserName", "type": { "code": "STRING" } }, + // ] + StructType row_type = 1; + + // If the read or SQL query began a transaction as a side-effect, the + // information about the new transaction is yielded here. + Transaction transaction = 2; + + // A SQL query can be parameterized. In PLAN mode, these parameters can be + // undeclared. This indicates the field names and types for those undeclared + // parameters in the SQL query. For example, a SQL query like `"SELECT * FROM + // Users where UserId = @userId and UserName = @userName "` could return a + // `undeclared_parameters` value like: + // + // "fields": [ + // { "name": "UserId", "type": { "code": "INT64" } }, + // { "name": "UserName", "type": { "code": "STRING" } }, + // ] + StructType undeclared_parameters = 3; +} + +// Additional statistics about a [ResultSet][google.spanner.v1.ResultSet] or +// [PartialResultSet][google.spanner.v1.PartialResultSet]. +message ResultSetStats { + // [QueryPlan][google.spanner.v1.QueryPlan] for the query associated with this + // result. + QueryPlan query_plan = 1; + + // Aggregated statistics from the execution of the query. Only present when + // the query is profiled. For example, a query could return the statistics as + // follows: + // + // { + // "rows_returned": "3", + // "elapsed_time": "1.22 secs", + // "cpu_time": "1.19 secs" + // } + google.protobuf.Struct query_stats = 2; + + // The number of rows modified by the DML statement. + oneof row_count { + // Standard DML returns an exact count of rows that were modified. + int64 row_count_exact = 3; + + // Partitioned DML doesn't offer exactly-once semantics, so it + // returns a lower bound of the rows modified. + int64 row_count_lower_bound = 4; + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner.proto new file mode 100644 index 00000000..c8e2d080 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner.proto @@ -0,0 +1,1389 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import public "google/spanner/v1/commit_response.proto"; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; +import "google/spanner/v1/keys.proto"; +import "google/spanner/v1/mutation.proto"; +import "google/spanner/v1/result_set.proto"; +import "google/spanner/v1/transaction.proto"; +import "google/spanner/v1/type.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "SpannerProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; +option (google.api.resource_definition) = { + type: "spanner.googleapis.com/Database" + pattern: "projects/{project}/instances/{instance}/databases/{database}" +}; + +// Cloud Spanner API +// +// The Cloud Spanner API can be used to manage sessions and execute +// transactions on data stored in Cloud Spanner databases. +service Spanner { + option (google.api.default_host) = "spanner.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/spanner.data"; + + // Creates a new session. A session can be used to perform + // transactions that read and/or modify data in a Cloud Spanner database. + // Sessions are meant to be reused for many consecutive + // transactions. + // + // Sessions can only execute one transaction at a time. To execute + // multiple concurrent read-write/write-only transactions, create + // multiple sessions. Note that standalone reads and queries use a + // transaction internally, and count toward the one transaction + // limit. + // + // Active sessions use additional server resources, so it is a good idea to + // delete idle and unneeded sessions. + // Aside from explicit deletes, Cloud Spanner may delete sessions for which no + // operations are sent for more than an hour. If a session is deleted, + // requests to it return `NOT_FOUND`. + // + // Idle sessions can be kept alive by sending a trivial SQL query + // periodically, e.g., `"SELECT 1"`. + rpc CreateSession(CreateSessionRequest) returns (Session) { + option (google.api.http) = { + post: "/v1/{database=projects/*/instances/*/databases/*}/sessions" + body: "*" + }; + option (google.api.method_signature) = "database"; + } + + // Creates multiple new sessions. + // + // This API can be used to initialize a session cache on the clients. + // See https://goo.gl/TgSFN2 for best practices on session cache management. + rpc BatchCreateSessions(BatchCreateSessionsRequest) + returns (BatchCreateSessionsResponse) { + option (google.api.http) = { + post: "/v1/{database=projects/*/instances/*/databases/*}/sessions:batchCreate" + body: "*" + }; + option (google.api.method_signature) = "database,session_count"; + } + + // Gets a session. Returns `NOT_FOUND` if the session does not exist. + // This is mainly useful for determining whether a session is still + // alive. + rpc GetSession(GetSessionRequest) returns (Session) { + option (google.api.http) = { + get: "/v1/{name=projects/*/instances/*/databases/*/sessions/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Lists all sessions in a given database. + rpc ListSessions(ListSessionsRequest) returns (ListSessionsResponse) { + option (google.api.http) = { + get: "/v1/{database=projects/*/instances/*/databases/*}/sessions" + }; + option (google.api.method_signature) = "database"; + } + + // Ends a session, releasing server resources associated with it. This will + // asynchronously trigger cancellation of any operations that are running with + // this session. + rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=projects/*/instances/*/databases/*/sessions/*}" + }; + option (google.api.method_signature) = "name"; + } + + // Executes an SQL statement, returning all results in a single reply. This + // method cannot be used to return a result set larger than 10 MiB; + // if the query yields more data than that, the query fails with + // a `FAILED_PRECONDITION` error. + // + // Operations inside read-write transactions might return `ABORTED`. If + // this occurs, the application should restart the transaction from + // the beginning. See [Transaction][google.spanner.v1.Transaction] for more + // details. + // + // Larger result sets can be fetched in streaming fashion by calling + // [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] + // instead. + rpc ExecuteSql(ExecuteSqlRequest) returns (ResultSet) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:executeSql" + body: "*" + }; + } + + // Like [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], except returns the + // result set as a stream. Unlike + // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], there is no limit on + // the size of the returned result set. However, no individual row in the + // result set can exceed 100 MiB, and no column value can exceed 10 MiB. + rpc ExecuteStreamingSql(ExecuteSqlRequest) returns (stream PartialResultSet) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:executeStreamingSql" + body: "*" + }; + } + + // Executes a batch of SQL DML statements. This method allows many statements + // to be run with lower latency than submitting them sequentially with + // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql]. + // + // Statements are executed in sequential order. A request can succeed even if + // a statement fails. The + // [ExecuteBatchDmlResponse.status][google.spanner.v1.ExecuteBatchDmlResponse.status] + // field in the response provides information about the statement that failed. + // Clients must inspect this field to determine whether an error occurred. + // + // Execution stops after the first failed statement; the remaining statements + // are not executed. + rpc ExecuteBatchDml(ExecuteBatchDmlRequest) + returns (ExecuteBatchDmlResponse) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:executeBatchDml" + body: "*" + }; + } + + // Reads rows from the database using key lookups and scans, as a + // simple key/value style alternative to + // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql]. This method cannot be + // used to return a result set larger than 10 MiB; if the read matches more + // data than that, the read fails with a `FAILED_PRECONDITION` + // error. + // + // Reads inside read-write transactions might return `ABORTED`. If + // this occurs, the application should restart the transaction from + // the beginning. See [Transaction][google.spanner.v1.Transaction] for more + // details. + // + // Larger result sets can be yielded in streaming fashion by calling + // [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead. + rpc Read(ReadRequest) returns (ResultSet) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:read" + body: "*" + }; + } + + // Like [Read][google.spanner.v1.Spanner.Read], except returns the result set + // as a stream. Unlike [Read][google.spanner.v1.Spanner.Read], there is no + // limit on the size of the returned result set. However, no individual row in + // the result set can exceed 100 MiB, and no column value can exceed + // 10 MiB. + rpc StreamingRead(ReadRequest) returns (stream PartialResultSet) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:streamingRead" + body: "*" + }; + } + + // Begins a new transaction. This step can often be skipped: + // [Read][google.spanner.v1.Spanner.Read], + // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and + // [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a + // side-effect. + rpc BeginTransaction(BeginTransactionRequest) returns (Transaction) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:beginTransaction" + body: "*" + }; + option (google.api.method_signature) = "session,options"; + } + + // Commits a transaction. The request includes the mutations to be + // applied to rows in the database. + // + // `Commit` might return an `ABORTED` error. This can occur at any time; + // commonly, the cause is conflicts with concurrent + // transactions. However, it can also happen for a variety of other + // reasons. If `Commit` returns `ABORTED`, the caller should re-attempt + // the transaction from the beginning, re-using the same session. + // + // On very rare occasions, `Commit` might return `UNKNOWN`. This can happen, + // for example, if the client job experiences a 1+ hour networking failure. + // At that point, Cloud Spanner has lost track of the transaction outcome and + // we recommend that you perform another read from the database to see the + // state of things as they are now. + rpc Commit(CommitRequest) returns (CommitResponse) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:commit" + body: "*" + }; + option (google.api.method_signature) = "session,transaction_id,mutations"; + option (google.api.method_signature) = + "session,single_use_transaction,mutations"; + } + + // Rolls back a transaction, releasing any locks it holds. It is a good + // idea to call this for any transaction that includes one or more + // [Read][google.spanner.v1.Spanner.Read] or + // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] requests and ultimately + // decides not to commit. + // + // `Rollback` returns `OK` if it successfully aborts the transaction, the + // transaction was already aborted, or the transaction is not + // found. `Rollback` never returns `ABORTED`. + rpc Rollback(RollbackRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:rollback" + body: "*" + }; + option (google.api.method_signature) = "session,transaction_id"; + } + + // Creates a set of partition tokens that can be used to execute a query + // operation in parallel. Each of the returned partition tokens can be used + // by [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] to + // specify a subset of the query result to read. The same session and + // read-only transaction must be used by the PartitionQueryRequest used to + // create the partition tokens and the ExecuteSqlRequests that use the + // partition tokens. + // + // Partition tokens become invalid when the session used to create them + // is deleted, is idle for too long, begins a new transaction, or becomes too + // old. When any of these happen, it is not possible to resume the query, and + // the whole operation must be restarted from the beginning. + rpc PartitionQuery(PartitionQueryRequest) returns (PartitionResponse) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:partitionQuery" + body: "*" + }; + } + + // Creates a set of partition tokens that can be used to execute a read + // operation in parallel. Each of the returned partition tokens can be used + // by [StreamingRead][google.spanner.v1.Spanner.StreamingRead] to specify a + // subset of the read result to read. The same session and read-only + // transaction must be used by the PartitionReadRequest used to create the + // partition tokens and the ReadRequests that use the partition tokens. There + // are no ordering guarantees on rows returned among the returned partition + // tokens, or even within each individual StreamingRead call issued with a + // partition_token. + // + // Partition tokens become invalid when the session used to create them + // is deleted, is idle for too long, begins a new transaction, or becomes too + // old. When any of these happen, it is not possible to resume the read, and + // the whole operation must be restarted from the beginning. + rpc PartitionRead(PartitionReadRequest) returns (PartitionResponse) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:partitionRead" + body: "*" + }; + } + + // Batches the supplied mutation groups in a collection of efficient + // transactions. All mutations in a group are committed atomically. However, + // mutations across groups can be committed non-atomically in an unspecified + // order and thus, they must be independent of each other. Partial failure is + // possible, i.e., some groups may have been committed successfully, while + // some may have failed. The results of individual batches are streamed into + // the response as the batches are applied. + // + // BatchWrite requests are not replay protected, meaning that each mutation + // group may be applied more than once. Replays of non-idempotent mutations + // may have undesirable effects. For example, replays of an insert mutation + // may produce an already exists error or if you use generated or commit + // timestamp-based keys, it may result in additional rows being added to the + // mutation's table. We recommend structuring your mutation groups to be + // idempotent to avoid this issue. + rpc BatchWrite(BatchWriteRequest) returns (stream BatchWriteResponse) { + option (google.api.http) = { + post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:batchWrite" + body: "*" + }; + option (google.api.method_signature) = "session,mutation_groups"; + } +} + +// The request for [CreateSession][google.spanner.v1.Spanner.CreateSession]. +message CreateSessionRequest { + // Required. The database in which the new session is created. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Required. The session to create. + Session session = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request for +// [BatchCreateSessions][google.spanner.v1.Spanner.BatchCreateSessions]. +message BatchCreateSessionsRequest { + // Required. The database in which the new sessions are created. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Parameters to be applied to each created session. + Session session_template = 2; + + // Required. The number of sessions to be created in this batch call. + // The API may return fewer than the requested number of sessions. If a + // specific number of sessions are desired, the client can make additional + // calls to BatchCreateSessions (adjusting + // [session_count][google.spanner.v1.BatchCreateSessionsRequest.session_count] + // as necessary). + int32 session_count = 3 [(google.api.field_behavior) = REQUIRED]; +} + +// The response for +// [BatchCreateSessions][google.spanner.v1.Spanner.BatchCreateSessions]. +message BatchCreateSessionsResponse { + // The freshly created sessions. + repeated Session session = 1; +} + +// A session in the Cloud Spanner API. +message Session { + option (google.api.resource) = { + type: "spanner.googleapis.com/Session" + pattern: "projects/{project}/instances/{instance}/databases/{database}/sessions/{session}" + }; + + // Output only. The name of the session. This is always system-assigned. + string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The labels for the session. + // + // * Label keys must be between 1 and 63 characters long and must conform to + // the following regular expression: `[a-z]([-a-z0-9]*[a-z0-9])?`. + // * Label values must be between 0 and 63 characters long and must conform + // to the regular expression `([a-z]([-a-z0-9]*[a-z0-9])?)?`. + // * No more than 64 labels can be associated with a given session. + // + // See https://goo.gl/xmQnxf for more information on and examples of labels. + map labels = 2; + + // Output only. The timestamp when the session is created. + google.protobuf.Timestamp create_time = 3 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The approximate timestamp when the session is last used. It is + // typically earlier than the actual last use time. + google.protobuf.Timestamp approximate_last_use_time = 4 + [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The database role which created this session. + string creator_role = 5; + + // Optional. If true, specifies a multiplexed session. A multiplexed session + // may be used for multiple, concurrent read-only operations but can not be + // used for read-write transactions, partitioned reads, or partitioned + // queries. Multiplexed sessions can be created via + // [CreateSession][google.spanner.v1.Spanner.CreateSession] but not via + // [BatchCreateSessions][google.spanner.v1.Spanner.BatchCreateSessions]. + // Multiplexed sessions may not be deleted nor listed. + bool multiplexed = 6 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request for [GetSession][google.spanner.v1.Spanner.GetSession]. +message GetSessionRequest { + // Required. The name of the session to retrieve. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; +} + +// The request for [ListSessions][google.spanner.v1.Spanner.ListSessions]. +message ListSessionsRequest { + // Required. The database in which to list sessions. + string database = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "spanner.googleapis.com/Database" + } + ]; + + // Number of sessions to be returned in the response. If 0 or less, defaults + // to the server's maximum allowed page size. + int32 page_size = 2; + + // If non-empty, `page_token` should contain a + // [next_page_token][google.spanner.v1.ListSessionsResponse.next_page_token] + // from a previous + // [ListSessionsResponse][google.spanner.v1.ListSessionsResponse]. + string page_token = 3; + + // An expression for filtering the results of the request. Filter rules are + // case insensitive. The fields eligible for filtering are: + // + // * `labels.key` where key is the name of a label + // + // Some examples of using filters are: + // + // * `labels.env:*` --> The session has the label "env". + // * `labels.env:dev` --> The session has the label "env" and the value of + // the label contains the string "dev". + string filter = 4; +} + +// The response for [ListSessions][google.spanner.v1.Spanner.ListSessions]. +message ListSessionsResponse { + // The list of requested sessions. + repeated Session sessions = 1; + + // `next_page_token` can be sent in a subsequent + // [ListSessions][google.spanner.v1.Spanner.ListSessions] call to fetch more + // of the matching sessions. + string next_page_token = 2; +} + +// The request for [DeleteSession][google.spanner.v1.Spanner.DeleteSession]. +message DeleteSessionRequest { + // Required. The name of the session to delete. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; +} + +// Common request options for various APIs. +message RequestOptions { + // The relative priority for requests. Note that priority is not applicable + // for [BeginTransaction][google.spanner.v1.Spanner.BeginTransaction]. + // + // The priority acts as a hint to the Cloud Spanner scheduler and does not + // guarantee priority or order of execution. For example: + // + // * Some parts of a write operation always execute at `PRIORITY_HIGH`, + // regardless of the specified priority. This may cause you to see an + // increase in high priority workload even when executing a low priority + // request. This can also potentially cause a priority inversion where a + // lower priority request will be fulfilled ahead of a higher priority + // request. + // * If a transaction contains multiple operations with different priorities, + // Cloud Spanner does not guarantee to process the higher priority + // operations first. There may be other constraints to satisfy, such as + // order of operations. + enum Priority { + // `PRIORITY_UNSPECIFIED` is equivalent to `PRIORITY_HIGH`. + PRIORITY_UNSPECIFIED = 0; + + // This specifies that the request is low priority. + PRIORITY_LOW = 1; + + // This specifies that the request is medium priority. + PRIORITY_MEDIUM = 2; + + // This specifies that the request is high priority. + PRIORITY_HIGH = 3; + } + + // Priority for the request. + Priority priority = 1; + + // A per-request tag which can be applied to queries or reads, used for + // statistics collection. + // Both request_tag and transaction_tag can be specified for a read or query + // that belongs to a transaction. + // This field is ignored for requests where it's not applicable (e.g. + // CommitRequest). + // Legal characters for `request_tag` values are all printable characters + // (ASCII 32 - 126) and the length of a request_tag is limited to 50 + // characters. Values that exceed this limit are truncated. + // Any leading underscore (_) characters will be removed from the string. + string request_tag = 2; + + // A tag used for statistics collection about this transaction. + // Both request_tag and transaction_tag can be specified for a read or query + // that belongs to a transaction. + // The value of transaction_tag should be the same for all requests belonging + // to the same transaction. + // If this request doesn't belong to any transaction, transaction_tag will be + // ignored. + // Legal characters for `transaction_tag` values are all printable characters + // (ASCII 32 - 126) and the length of a transaction_tag is limited to 50 + // characters. Values that exceed this limit are truncated. + // Any leading underscore (_) characters will be removed from the string. + string transaction_tag = 3; +} + +// The DirectedReadOptions can be used to indicate which replicas or regions +// should be used for non-transactional reads or queries. +// +// DirectedReadOptions may only be specified for a read-only transaction, +// otherwise the API will return an `INVALID_ARGUMENT` error. +message DirectedReadOptions { + // The directed read replica selector. + // Callers must provide one or more of the following fields for replica + // selection: + // + // * `location` - The location must be one of the regions within the + // multi-region configuration of your database. + // * `type` - The type of the replica. + // + // Some examples of using replica_selectors are: + // + // * `location:us-east1` --> The "us-east1" replica(s) of any available type + // will be used to process the request. + // * `type:READ_ONLY` --> The "READ_ONLY" type replica(s) in nearest + // available location will be used to process the + // request. + // * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s) + // in location "us-east1" will be used to process + // the request. + message ReplicaSelection { + // Indicates the type of replica. + enum Type { + // Not specified. + TYPE_UNSPECIFIED = 0; + + // Read-write replicas support both reads and writes. + READ_WRITE = 1; + + // Read-only replicas only support reads (not writes). + READ_ONLY = 2; + } + + // The location or region of the serving requests, e.g. "us-east1". + string location = 1; + + // The type of replica. + Type type = 2; + } + + // An IncludeReplicas contains a repeated set of ReplicaSelection which + // indicates the order in which replicas should be considered. + message IncludeReplicas { + // The directed read replica selector. + repeated ReplicaSelection replica_selections = 1; + + // If true, Spanner will not route requests to a replica outside the + // include_replicas list when all of the specified replicas are unavailable + // or unhealthy. Default value is `false`. + bool auto_failover_disabled = 2; + } + + // An ExcludeReplicas contains a repeated set of ReplicaSelection that should + // be excluded from serving requests. + message ExcludeReplicas { + // The directed read replica selector. + repeated ReplicaSelection replica_selections = 1; + } + + // Required. At most one of either include_replicas or exclude_replicas + // should be present in the message. + oneof replicas { + // Include_replicas indicates the order of replicas (as they appear in + // this list) to process the request. If auto_failover_disabled is set to + // true and all replicas are exhausted without finding a healthy replica, + // Spanner will wait for a replica in the list to become available, requests + // may fail due to `DEADLINE_EXCEEDED` errors. + IncludeReplicas include_replicas = 1; + + // Exclude_replicas indicates that specified replicas should be excluded + // from serving requests. Spanner will not route requests to the replicas + // in this list. + ExcludeReplicas exclude_replicas = 2; + } +} + +// The request for [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and +// [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql]. +message ExecuteSqlRequest { + // Mode in which the statement must be processed. + enum QueryMode { + // The default mode. Only the statement results are returned. + NORMAL = 0; + + // This mode returns only the query plan, without any results or + // execution statistics information. + PLAN = 1; + + // This mode returns the query plan, overall execution statistics, + // operator level execution statistics along with the results. This has a + // performance overhead compared to the other modes. It is not recommended + // to use this mode for production traffic. + PROFILE = 2; + + // This mode returns the overall (but not operator-level) execution + // statistics along with the results. + WITH_STATS = 3; + + // This mode returns the query plan, overall (but not operator-level) + // execution statistics along with the results. + WITH_PLAN_AND_STATS = 4; + } + + // Query optimizer configuration. + message QueryOptions { + // An option to control the selection of optimizer version. + // + // This parameter allows individual queries to pick different query + // optimizer versions. + // + // Specifying `latest` as a value instructs Cloud Spanner to use the + // latest supported query optimizer version. If not specified, Cloud Spanner + // uses the optimizer version set at the database level options. Any other + // positive integer (from the list of supported optimizer versions) + // overrides the default optimizer version for query execution. + // + // The list of supported optimizer versions can be queried from + // SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS. + // + // Executing a SQL statement with an invalid optimizer version fails with + // an `INVALID_ARGUMENT` error. + // + // See + // https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer + // for more information on managing the query optimizer. + // + // The `optimizer_version` statement hint has precedence over this setting. + string optimizer_version = 1; + + // An option to control the selection of optimizer statistics package. + // + // This parameter allows individual queries to use a different query + // optimizer statistics package. + // + // Specifying `latest` as a value instructs Cloud Spanner to use the latest + // generated statistics package. If not specified, Cloud Spanner uses + // the statistics package set at the database level options, or the latest + // package if the database option is not set. + // + // The statistics package requested by the query has to be exempt from + // garbage collection. This can be achieved with the following DDL + // statement: + // + // ``` + // ALTER STATISTICS SET OPTIONS (allow_gc=false) + // ``` + // + // The list of available statistics packages can be queried from + // `INFORMATION_SCHEMA.SPANNER_STATISTICS`. + // + // Executing a SQL statement with an invalid optimizer statistics package + // or with a statistics package that allows garbage collection fails with + // an `INVALID_ARGUMENT` error. + string optimizer_statistics_package = 2; + } + + // Required. The session in which the SQL query should be performed. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // The transaction to use. + // + // For queries, if none is provided, the default is a temporary read-only + // transaction with strong concurrency. + // + // Standard DML statements require a read-write transaction. To protect + // against replays, single-use transactions are not supported. The caller + // must either supply an existing transaction ID or begin a new transaction. + // + // Partitioned DML requires an existing Partitioned DML transaction ID. + TransactionSelector transaction = 2; + + // Required. The SQL string. + string sql = 3 [(google.api.field_behavior) = REQUIRED]; + + // Parameter names and values that bind to placeholders in the SQL string. + // + // A parameter placeholder consists of the `@` character followed by the + // parameter name (for example, `@firstName`). Parameter names must conform + // to the naming requirements of identifiers as specified at + // https://cloud.google.com/spanner/docs/lexical#identifiers. + // + // Parameters can appear anywhere that a literal value is expected. The same + // parameter name can be used more than once, for example: + // + // `"WHERE id > @msg_id AND id < @msg_id + 100"` + // + // It is an error to execute a SQL statement with unbound parameters. + google.protobuf.Struct params = 4; + + // It is not always possible for Cloud Spanner to infer the right SQL type + // from a JSON value. For example, values of type `BYTES` and values + // of type `STRING` both appear in + // [params][google.spanner.v1.ExecuteSqlRequest.params] as JSON strings. + // + // In these cases, `param_types` can be used to specify the exact + // SQL type for some or all of the SQL statement parameters. See the + // definition of [Type][google.spanner.v1.Type] for more information + // about SQL types. + map param_types = 5; + + // If this request is resuming a previously interrupted SQL statement + // execution, `resume_token` should be copied from the last + // [PartialResultSet][google.spanner.v1.PartialResultSet] yielded before the + // interruption. Doing this enables the new SQL statement execution to resume + // where the last one left off. The rest of the request parameters must + // exactly match the request that yielded this token. + bytes resume_token = 6; + + // Used to control the amount of debugging information returned in + // [ResultSetStats][google.spanner.v1.ResultSetStats]. If + // [partition_token][google.spanner.v1.ExecuteSqlRequest.partition_token] is + // set, [query_mode][google.spanner.v1.ExecuteSqlRequest.query_mode] can only + // be set to + // [QueryMode.NORMAL][google.spanner.v1.ExecuteSqlRequest.QueryMode.NORMAL]. + QueryMode query_mode = 7; + + // If present, results will be restricted to the specified partition + // previously created using PartitionQuery(). There must be an exact + // match for the values of fields common to this message and the + // PartitionQueryRequest message used to create this partition_token. + bytes partition_token = 8; + + // A per-transaction sequence number used to identify this request. This field + // makes each request idempotent such that if the request is received multiple + // times, at most one will succeed. + // + // The sequence number must be monotonically increasing within the + // transaction. If a request arrives for the first time with an out-of-order + // sequence number, the transaction may be aborted. Replays of previously + // handled requests will yield the same response as the first execution. + // + // Required for DML statements. Ignored for queries. + int64 seqno = 9; + + // Query optimizer configuration to use for the given query. + QueryOptions query_options = 10; + + // Common options for this request. + RequestOptions request_options = 11; + + // Directed read options for this request. + DirectedReadOptions directed_read_options = 15; + + // If this is for a partitioned query and this field is set to `true`, the + // request is executed with Spanner Data Boost independent compute resources. + // + // If the field is set to `true` but the request does not set + // `partition_token`, the API returns an `INVALID_ARGUMENT` error. + bool data_boost_enabled = 16; + + // Optional. If set to true, this statement marks the end of the transaction. + // The transaction should be committed or aborted after this statement + // executes, and attempts to execute any other requests against this + // transaction (including reads and queries) will be rejected. + // + // For DML statements, setting this option may cause some error reporting to + // be deferred until commit time (e.g. validation of unique constraints). + // Given this, successful execution of a DML statement should not be assumed + // until a subsequent Commit call completes successfully. + bool last_statement = 17 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request for [ExecuteBatchDml][google.spanner.v1.Spanner.ExecuteBatchDml]. +message ExecuteBatchDmlRequest { + // A single DML statement. + message Statement { + // Required. The DML string. + string sql = 1 [(google.api.field_behavior) = REQUIRED]; + + // Parameter names and values that bind to placeholders in the DML string. + // + // A parameter placeholder consists of the `@` character followed by the + // parameter name (for example, `@firstName`). Parameter names can contain + // letters, numbers, and underscores. + // + // Parameters can appear anywhere that a literal value is expected. The + // same parameter name can be used more than once, for example: + // + // `"WHERE id > @msg_id AND id < @msg_id + 100"` + // + // It is an error to execute a SQL statement with unbound parameters. + google.protobuf.Struct params = 2; + + // It is not always possible for Cloud Spanner to infer the right SQL type + // from a JSON value. For example, values of type `BYTES` and values + // of type `STRING` both appear in + // [params][google.spanner.v1.ExecuteBatchDmlRequest.Statement.params] as + // JSON strings. + // + // In these cases, `param_types` can be used to specify the exact + // SQL type for some or all of the SQL statement parameters. See the + // definition of [Type][google.spanner.v1.Type] for more information + // about SQL types. + map param_types = 3; + } + + // Required. The session in which the DML statements should be performed. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Required. The transaction to use. Must be a read-write transaction. + // + // To protect against replays, single-use transactions are not supported. The + // caller must either supply an existing transaction ID or begin a new + // transaction. + TransactionSelector transaction = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The list of statements to execute in this batch. Statements are + // executed serially, such that the effects of statement `i` are visible to + // statement `i+1`. Each statement must be a DML statement. Execution stops at + // the first failed statement; the remaining statements are not executed. + // + // Callers must provide at least one statement. + repeated Statement statements = 3 [(google.api.field_behavior) = REQUIRED]; + + // Required. A per-transaction sequence number used to identify this request. + // This field makes each request idempotent such that if the request is + // received multiple times, at most one will succeed. + // + // The sequence number must be monotonically increasing within the + // transaction. If a request arrives for the first time with an out-of-order + // sequence number, the transaction may be aborted. Replays of previously + // handled requests will yield the same response as the first execution. + int64 seqno = 4 [(google.api.field_behavior) = REQUIRED]; + + // Common options for this request. + RequestOptions request_options = 5; + + // Optional. If set to true, this request marks the end of the transaction. + // The transaction should be committed or aborted after these statements + // execute, and attempts to execute any other requests against this + // transaction (including reads and queries) will be rejected. + // + // Setting this option may cause some error reporting to be deferred until + // commit time (e.g. validation of unique constraints). Given this, successful + // execution of statements should not be assumed until a subsequent Commit + // call completes successfully. + bool last_statements = 6 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for +// [ExecuteBatchDml][google.spanner.v1.Spanner.ExecuteBatchDml]. Contains a list +// of [ResultSet][google.spanner.v1.ResultSet] messages, one for each DML +// statement that has successfully executed, in the same order as the statements +// in the request. If a statement fails, the status in the response body +// identifies the cause of the failure. +// +// To check for DML statements that failed, use the following approach: +// +// 1. Check the status in the response message. The +// [google.rpc.Code][google.rpc.Code] enum +// value `OK` indicates that all statements were executed successfully. +// 2. If the status was not `OK`, check the number of result sets in the +// response. If the response contains `N` +// [ResultSet][google.spanner.v1.ResultSet] messages, then statement `N+1` in +// the request failed. +// +// Example 1: +// +// * Request: 5 DML statements, all executed successfully. +// * Response: 5 [ResultSet][google.spanner.v1.ResultSet] messages, with the +// status `OK`. +// +// Example 2: +// +// * Request: 5 DML statements. The third statement has a syntax error. +// * Response: 2 [ResultSet][google.spanner.v1.ResultSet] messages, and a syntax +// error (`INVALID_ARGUMENT`) +// status. The number of [ResultSet][google.spanner.v1.ResultSet] messages +// indicates that the third statement failed, and the fourth and fifth +// statements were not executed. +message ExecuteBatchDmlResponse { + // One [ResultSet][google.spanner.v1.ResultSet] for each statement in the + // request that ran successfully, in the same order as the statements in the + // request. Each [ResultSet][google.spanner.v1.ResultSet] does not contain any + // rows. The [ResultSetStats][google.spanner.v1.ResultSetStats] in each + // [ResultSet][google.spanner.v1.ResultSet] contain the number of rows + // modified by the statement. + // + // Only the first [ResultSet][google.spanner.v1.ResultSet] in the response + // contains valid [ResultSetMetadata][google.spanner.v1.ResultSetMetadata]. + repeated ResultSet result_sets = 1; + + // If all DML statements are executed successfully, the status is `OK`. + // Otherwise, the error status of the first failed statement. + google.rpc.Status status = 2; + + // Optional. A precommit token will be included if the read-write transaction + // is on a multiplexed session. + // The precommit token with the highest sequence number from this transaction + // attempt should be passed to the + // [Commit][google.spanner.v1.Spanner.Commit] request for this transaction. + // This feature is not yet supported and will result in an UNIMPLEMENTED + // error. + MultiplexedSessionPrecommitToken precommit_token = 3 + [(google.api.field_behavior) = OPTIONAL]; +} + +// Options for a PartitionQueryRequest and +// PartitionReadRequest. +message PartitionOptions { + // **Note:** This hint is currently ignored by PartitionQuery and + // PartitionRead requests. + // + // The desired data size for each partition generated. The default for this + // option is currently 1 GiB. This is only a hint. The actual size of each + // partition may be smaller or larger than this size request. + int64 partition_size_bytes = 1; + + // **Note:** This hint is currently ignored by PartitionQuery and + // PartitionRead requests. + // + // The desired maximum number of partitions to return. For example, this may + // be set to the number of workers available. The default for this option + // is currently 10,000. The maximum value is currently 200,000. This is only + // a hint. The actual number of partitions returned may be smaller or larger + // than this maximum count request. + int64 max_partitions = 2; +} + +// The request for [PartitionQuery][google.spanner.v1.Spanner.PartitionQuery] +message PartitionQueryRequest { + // Required. The session used to create the partitions. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Read only snapshot transactions are supported, read/write and single use + // transactions are not. + TransactionSelector transaction = 2; + + // Required. The query request to generate partitions for. The request will + // fail if the query is not root partitionable. For a query to be root + // partitionable, it needs to satisfy a few conditions. For example, if the + // query execution plan contains a distributed union operator, then it must be + // the first operator in the plan. For more information about other + // conditions, see [Read data in + // parallel](https://cloud.google.com/spanner/docs/reads#read_data_in_parallel). + // + // The query request must not contain DML commands, such as INSERT, UPDATE, or + // DELETE. Use + // [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] with a + // PartitionedDml transaction for large, partition-friendly DML operations. + string sql = 3 [(google.api.field_behavior) = REQUIRED]; + + // Parameter names and values that bind to placeholders in the SQL string. + // + // A parameter placeholder consists of the `@` character followed by the + // parameter name (for example, `@firstName`). Parameter names can contain + // letters, numbers, and underscores. + // + // Parameters can appear anywhere that a literal value is expected. The same + // parameter name can be used more than once, for example: + // + // `"WHERE id > @msg_id AND id < @msg_id + 100"` + // + // It is an error to execute a SQL statement with unbound parameters. + google.protobuf.Struct params = 4; + + // It is not always possible for Cloud Spanner to infer the right SQL type + // from a JSON value. For example, values of type `BYTES` and values + // of type `STRING` both appear in + // [params][google.spanner.v1.PartitionQueryRequest.params] as JSON strings. + // + // In these cases, `param_types` can be used to specify the exact + // SQL type for some or all of the SQL query parameters. See the + // definition of [Type][google.spanner.v1.Type] for more information + // about SQL types. + map param_types = 5; + + // Additional options that affect how many partitions are created. + PartitionOptions partition_options = 6; +} + +// The request for [PartitionRead][google.spanner.v1.Spanner.PartitionRead] +message PartitionReadRequest { + // Required. The session used to create the partitions. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Read only snapshot transactions are supported, read/write and single use + // transactions are not. + TransactionSelector transaction = 2; + + // Required. The name of the table in the database to be read. + string table = 3 [(google.api.field_behavior) = REQUIRED]; + + // If non-empty, the name of an index on + // [table][google.spanner.v1.PartitionReadRequest.table]. This index is used + // instead of the table primary key when interpreting + // [key_set][google.spanner.v1.PartitionReadRequest.key_set] and sorting + // result rows. See [key_set][google.spanner.v1.PartitionReadRequest.key_set] + // for further information. + string index = 4; + + // The columns of [table][google.spanner.v1.PartitionReadRequest.table] to be + // returned for each row matching this request. + repeated string columns = 5; + + // Required. `key_set` identifies the rows to be yielded. `key_set` names the + // primary keys of the rows in + // [table][google.spanner.v1.PartitionReadRequest.table] to be yielded, unless + // [index][google.spanner.v1.PartitionReadRequest.index] is present. If + // [index][google.spanner.v1.PartitionReadRequest.index] is present, then + // [key_set][google.spanner.v1.PartitionReadRequest.key_set] instead names + // index keys in [index][google.spanner.v1.PartitionReadRequest.index]. + // + // It is not an error for the `key_set` to name rows that do not + // exist in the database. Read yields nothing for nonexistent rows. + KeySet key_set = 6 [(google.api.field_behavior) = REQUIRED]; + + // Additional options that affect how many partitions are created. + PartitionOptions partition_options = 9; +} + +// Information returned for each partition returned in a +// PartitionResponse. +message Partition { + // This token can be passed to Read, StreamingRead, ExecuteSql, or + // ExecuteStreamingSql requests to restrict the results to those identified by + // this partition token. + bytes partition_token = 1; +} + +// The response for [PartitionQuery][google.spanner.v1.Spanner.PartitionQuery] +// or [PartitionRead][google.spanner.v1.Spanner.PartitionRead] +message PartitionResponse { + // Partitions created by this request. + repeated Partition partitions = 1; + + // Transaction created by this request. + Transaction transaction = 2; +} + +// The request for [Read][google.spanner.v1.Spanner.Read] and +// [StreamingRead][google.spanner.v1.Spanner.StreamingRead]. +message ReadRequest { + // An option to control the order in which rows are returned from a read. + enum OrderBy { + // Default value. + // + // ORDER_BY_UNSPECIFIED is equivalent to ORDER_BY_PRIMARY_KEY. + ORDER_BY_UNSPECIFIED = 0; + + // Read rows are returned in primary key order. + // + // In the event that this option is used in conjunction with the + // `partition_token` field, the API will return an `INVALID_ARGUMENT` error. + ORDER_BY_PRIMARY_KEY = 1; + + // Read rows are returned in any order. + ORDER_BY_NO_ORDER = 2; + } + + // A lock hint mechanism for reads done within a transaction. + enum LockHint { + // Default value. + // + // LOCK_HINT_UNSPECIFIED is equivalent to LOCK_HINT_SHARED. + LOCK_HINT_UNSPECIFIED = 0; + + // Acquire shared locks. + // + // By default when you perform a read as part of a read-write transaction, + // Spanner acquires shared read locks, which allows other reads to still + // access the data until your transaction is ready to commit. When your + // transaction is committing and writes are being applied, the transaction + // attempts to upgrade to an exclusive lock for any data you are writing. + // For more information about locks, see [Lock + // modes](https://cloud.google.com/spanner/docs/introspection/lock-statistics#explain-lock-modes). + LOCK_HINT_SHARED = 1; + + // Acquire exclusive locks. + // + // Requesting exclusive locks is beneficial if you observe high write + // contention, which means you notice that multiple transactions are + // concurrently trying to read and write to the same data, resulting in a + // large number of aborts. This problem occurs when two transactions + // initially acquire shared locks and then both try to upgrade to exclusive + // locks at the same time. In this situation both transactions are waiting + // for the other to give up their lock, resulting in a deadlocked situation. + // Spanner is able to detect this occurring and force one of the + // transactions to abort. However, this is a slow and expensive operation + // and results in lower performance. In this case it makes sense to acquire + // exclusive locks at the start of the transaction because then when + // multiple transactions try to act on the same data, they automatically get + // serialized. Each transaction waits its turn to acquire the lock and + // avoids getting into deadlock situations. + // + // Because the exclusive lock hint is just a hint, it should not be + // considered equivalent to a mutex. In other words, you should not use + // Spanner exclusive locks as a mutual exclusion mechanism for the execution + // of code outside of Spanner. + // + // **Note:** Request exclusive locks judiciously because they block others + // from reading that data for the entire transaction, rather than just when + // the writes are being performed. Unless you observe high write contention, + // you should use the default of shared read locks so you don't prematurely + // block other clients from reading the data that you're writing to. + LOCK_HINT_EXCLUSIVE = 2; + } + + // Required. The session in which the read should be performed. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // The transaction to use. If none is provided, the default is a + // temporary read-only transaction with strong concurrency. + TransactionSelector transaction = 2; + + // Required. The name of the table in the database to be read. + string table = 3 [(google.api.field_behavior) = REQUIRED]; + + // If non-empty, the name of an index on + // [table][google.spanner.v1.ReadRequest.table]. This index is used instead of + // the table primary key when interpreting + // [key_set][google.spanner.v1.ReadRequest.key_set] and sorting result rows. + // See [key_set][google.spanner.v1.ReadRequest.key_set] for further + // information. + string index = 4; + + // Required. The columns of [table][google.spanner.v1.ReadRequest.table] to be + // returned for each row matching this request. + repeated string columns = 5 [(google.api.field_behavior) = REQUIRED]; + + // Required. `key_set` identifies the rows to be yielded. `key_set` names the + // primary keys of the rows in [table][google.spanner.v1.ReadRequest.table] to + // be yielded, unless [index][google.spanner.v1.ReadRequest.index] is present. + // If [index][google.spanner.v1.ReadRequest.index] is present, then + // [key_set][google.spanner.v1.ReadRequest.key_set] instead names index keys + // in [index][google.spanner.v1.ReadRequest.index]. + // + // If the [partition_token][google.spanner.v1.ReadRequest.partition_token] + // field is empty, rows are yielded in table primary key order (if + // [index][google.spanner.v1.ReadRequest.index] is empty) or index key order + // (if [index][google.spanner.v1.ReadRequest.index] is non-empty). If the + // [partition_token][google.spanner.v1.ReadRequest.partition_token] field is + // not empty, rows will be yielded in an unspecified order. + // + // It is not an error for the `key_set` to name rows that do not + // exist in the database. Read yields nothing for nonexistent rows. + KeySet key_set = 6 [(google.api.field_behavior) = REQUIRED]; + + // If greater than zero, only the first `limit` rows are yielded. If `limit` + // is zero, the default is no limit. A limit cannot be specified if + // `partition_token` is set. + int64 limit = 8; + + // If this request is resuming a previously interrupted read, + // `resume_token` should be copied from the last + // [PartialResultSet][google.spanner.v1.PartialResultSet] yielded before the + // interruption. Doing this enables the new read to resume where the last read + // left off. The rest of the request parameters must exactly match the request + // that yielded this token. + bytes resume_token = 9; + + // If present, results will be restricted to the specified partition + // previously created using PartitionRead(). There must be an exact + // match for the values of fields common to this message and the + // PartitionReadRequest message used to create this partition_token. + bytes partition_token = 10; + + // Common options for this request. + RequestOptions request_options = 11; + + // Directed read options for this request. + DirectedReadOptions directed_read_options = 14; + + // If this is for a partitioned read and this field is set to `true`, the + // request is executed with Spanner Data Boost independent compute resources. + // + // If the field is set to `true` but the request does not set + // `partition_token`, the API returns an `INVALID_ARGUMENT` error. + bool data_boost_enabled = 15; + + // Optional. Order for the returned rows. + // + // By default, Spanner will return result rows in primary key order except for + // PartitionRead requests. For applications that do not require rows to be + // returned in primary key (`ORDER_BY_PRIMARY_KEY`) order, setting + // `ORDER_BY_NO_ORDER` option allows Spanner to optimize row retrieval, + // resulting in lower latencies in certain cases (e.g. bulk point lookups). + OrderBy order_by = 16 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Lock Hint for the request, it can only be used with read-write + // transactions. + LockHint lock_hint = 17 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request for +// [BeginTransaction][google.spanner.v1.Spanner.BeginTransaction]. +message BeginTransactionRequest { + // Required. The session in which the transaction runs. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Required. Options for the new transaction. + TransactionOptions options = 2 [(google.api.field_behavior) = REQUIRED]; + + // Common options for this request. + // Priority is ignored for this request. Setting the priority in this + // request_options struct will not do anything. To set the priority for a + // transaction, set it on the reads and writes that are part of this + // transaction instead. + RequestOptions request_options = 3; + + // Optional. Required for read-write transactions on a multiplexed session + // that commit mutations but do not perform any reads or queries. Clients + // should randomly select one of the mutations from the mutation set and send + // it as a part of this request. + // This feature is not yet supported and will result in an UNIMPLEMENTED + // error. + Mutation mutation_key = 4 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request for [Commit][google.spanner.v1.Spanner.Commit]. +message CommitRequest { + // Required. The session in which the transaction to be committed is running. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Required. The transaction in which to commit. + oneof transaction { + // Commit a previously-started transaction. + bytes transaction_id = 2; + + // Execute mutations in a temporary transaction. Note that unlike + // commit of a previously-started transaction, commit with a + // temporary transaction is non-idempotent. That is, if the + // `CommitRequest` is sent to Cloud Spanner more than once (for + // instance, due to retries in the application, or in the + // transport library), it is possible that the mutations are + // executed more than once. If this is undesirable, use + // [BeginTransaction][google.spanner.v1.Spanner.BeginTransaction] and + // [Commit][google.spanner.v1.Spanner.Commit] instead. + TransactionOptions single_use_transaction = 3; + } + + // The mutations to be executed when this transaction commits. All + // mutations are applied atomically, in the order they appear in + // this list. + repeated Mutation mutations = 4; + + // If `true`, then statistics related to the transaction will be included in + // the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. + // Default value is `false`. + bool return_commit_stats = 5; + + // Optional. The amount of latency this request is willing to incur in order + // to improve throughput. If this field is not set, Spanner assumes requests + // are relatively latency sensitive and automatically determines an + // appropriate delay time. You can specify a batching delay value between 0 + // and 500 ms. + google.protobuf.Duration max_commit_delay = 8 + [(google.api.field_behavior) = OPTIONAL]; + + // Common options for this request. + RequestOptions request_options = 6; + + // Optional. If the read-write transaction was executed on a multiplexed + // session, the precommit token with the highest sequence number received in + // this transaction attempt, should be included here. Failing to do so will + // result in a FailedPrecondition error. + // This feature is not yet supported and will result in an UNIMPLEMENTED + // error. + MultiplexedSessionPrecommitToken precommit_token = 9 + [(google.api.field_behavior) = OPTIONAL]; +} + +// The request for [Rollback][google.spanner.v1.Spanner.Rollback]. +message RollbackRequest { + // Required. The session in which the transaction to roll back is running. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Required. The transaction to roll back. + bytes transaction_id = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request for [BatchWrite][google.spanner.v1.Spanner.BatchWrite]. +message BatchWriteRequest { + // A group of mutations to be committed together. Related mutations should be + // placed in a group. For example, two mutations inserting rows with the same + // primary key prefix in both parent and child tables are related. + message MutationGroup { + // Required. The mutations in this group. + repeated Mutation mutations = 1 [(google.api.field_behavior) = REQUIRED]; + } + + // Required. The session in which the batch request is to be run. + string session = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" } + ]; + + // Common options for this request. + RequestOptions request_options = 3; + + // Required. The groups of mutations to be applied. + repeated MutationGroup mutation_groups = 4 + [(google.api.field_behavior) = REQUIRED]; + + // Optional. When `exclude_txn_from_change_streams` is set to `true`: + // * Mutations from all transactions in this batch write operation will not + // be recorded in change streams with DDL option `allow_txn_exclusion=true` + // that are tracking columns modified by these transactions. + // * Mutations from all transactions in this batch write operation will be + // recorded in change streams with DDL option `allow_txn_exclusion=false or + // not set` that are tracking columns modified by these transactions. + // + // When `exclude_txn_from_change_streams` is set to `false` or not set, + // mutations from all transactions in this batch write operation will be + // recorded in all change streams that are tracking columns modified by these + // transactions. + bool exclude_txn_from_change_streams = 5 + [(google.api.field_behavior) = OPTIONAL]; +} + +// The result of applying a batch of mutations. +message BatchWriteResponse { + // The mutation groups applied in this batch. The values index into the + // `mutation_groups` field in the corresponding `BatchWriteRequest`. + repeated int32 indexes = 1; + + // An `OK` status indicates success. Any other status indicates a failure. + google.rpc.Status status = 2; + + // The commit timestamp of the transaction that applied this batch. + // Present if `status` is `OK`, absent otherwise. + google.protobuf.Timestamp commit_timestamp = 3; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner.yaml new file mode 100644 index 00000000..06fe8233 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner.yaml @@ -0,0 +1,63 @@ +type: google.api.Service +config_version: 3 +name: spanner.googleapis.com +title: Cloud Spanner API + +apis: +- name: google.spanner.v1.Spanner + +documentation: + summary: |- + Cloud Spanner is a managed, mission-critical, globally consistent and + scalable relational database service. + +backend: + rules: + - selector: 'google.longrunning.Operations.*' + deadline: 3600.0 + - selector: 'google.spanner.v1.Spanner.*' + deadline: 3600.0 + +http: + rules: + - selector: google.longrunning.Operations.CancelOperation + post: '/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel' + additional_bindings: + - post: '/v1/{name=projects/*/instances/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instances/*/backups/*/operations/*}:cancel' + - post: '/v1/{name=projects/*/instanceConfigs/*/operations/*}:cancel' + - selector: google.longrunning.Operations.DeleteOperation + delete: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - delete: '/v1/{name=projects/*/instances/*/operations/*}' + - delete: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - delete: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - selector: google.longrunning.Operations.GetOperation + get: '/v1/{name=projects/*/instances/*/databases/*/operations/*}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations/*}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations/*}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations/*}' + - selector: google.longrunning.Operations.ListOperations + get: '/v1/{name=projects/*/instances/*/databases/*/operations}' + additional_bindings: + - get: '/v1/{name=projects/*/instances/*/operations}' + - get: '/v1/{name=projects/*/instances/*/backups/*/operations}' + - get: '/v1/{name=projects/*/instanceConfigs/*/operations}' + +authentication: + rules: + - selector: 'google.longrunning.Operations.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.admin + - selector: 'google.spanner.v1.Spanner.*' + oauth: + canonical_scopes: |- + https://www.googleapis.com/auth/cloud-platform, + https://www.googleapis.com/auth/spanner.data + +publishing: + new_issue_uri: https://issuetracker.google.com/issues/new?component=190851&template=0 + documentation_uri: https://cloud.google.com/spanner/ diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner_gapic.yaml b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner_gapic.yaml new file mode 100644 index 00000000..d239cb82 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner_gapic.yaml @@ -0,0 +1,6 @@ +type: com.google.api.codegen.ConfigProto +config_schema_version: 2.0.0 +language_settings: + java: + package_name: com.google.cloud.spanner.v1 + release_level: GA diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner_grpc_service_config.json b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner_grpc_service_config.json new file mode 100755 index 00000000..68853556 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/spanner_grpc_service_config.json @@ -0,0 +1,115 @@ +{ + "methodConfig": [ + { + "name": [ + { + "service": "google.spanner.v1.Spanner", + "method": "ExecuteStreamingSql" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "StreamingRead" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "BatchWrite" + } + ], + "timeout": "3600s" + }, + { + "name": [ + { + "service": "google.spanner.v1.Spanner", + "method": "Commit" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "ListSessions" + } + ], + "timeout": "3600s", + "retryPolicy": { + "initialBackoff": "0.250s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "RESOURCE_EXHAUSTED" + ] + } + }, + { + "name": [ + { + "service": "google.spanner.v1.Spanner", + "method": "BatchCreateSessions" + } + ], + "timeout": "60s", + "retryPolicy": { + "initialBackoff": "0.250s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "RESOURCE_EXHAUSTED" + ] + } + }, + { + "name": [ + { + "service": "google.spanner.v1.Spanner", + "method": "CreateSession" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "GetSession" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "DeleteSession" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "ExecuteSql" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "ExecuteBatchDml" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "Read" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "BeginTransaction" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "Rollback" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "PartitionQuery" + }, + { + "service": "google.spanner.v1.Spanner", + "method": "PartitionRead" + } + ], + "timeout": "30s", + "retryPolicy": { + "initialBackoff": "0.250s", + "maxBackoff": "32s", + "backoffMultiplier": 1.3, + "retryableStatusCodes": [ + "UNAVAILABLE", + "RESOURCE_EXHAUSTED" + ] + } + } + ] +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/transaction.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/transaction.proto new file mode 100644 index 00000000..612e491a --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/transaction.proto @@ -0,0 +1,622 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/api/field_behavior.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "TransactionProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// Transactions: +// +// Each session can have at most one active transaction at a time (note that +// standalone reads and queries use a transaction internally and do count +// towards the one transaction limit). After the active transaction is +// completed, the session can immediately be re-used for the next transaction. +// It is not necessary to create a new session for each transaction. +// +// Transaction modes: +// +// Cloud Spanner supports three transaction modes: +// +// 1. Locking read-write. This type of transaction is the only way +// to write data into Cloud Spanner. These transactions rely on +// pessimistic locking and, if necessary, two-phase commit. +// Locking read-write transactions may abort, requiring the +// application to retry. +// +// 2. Snapshot read-only. Snapshot read-only transactions provide guaranteed +// consistency across several reads, but do not allow +// writes. Snapshot read-only transactions can be configured to read at +// timestamps in the past, or configured to perform a strong read +// (where Spanner will select a timestamp such that the read is +// guaranteed to see the effects of all transactions that have committed +// before the start of the read). Snapshot read-only transactions do not +// need to be committed. +// +// Queries on change streams must be performed with the snapshot read-only +// transaction mode, specifying a strong read. Please see +// [TransactionOptions.ReadOnly.strong][google.spanner.v1.TransactionOptions.ReadOnly.strong] +// for more details. +// +// 3. Partitioned DML. This type of transaction is used to execute +// a single Partitioned DML statement. Partitioned DML partitions +// the key space and runs the DML statement over each partition +// in parallel using separate, internal transactions that commit +// independently. Partitioned DML transactions do not need to be +// committed. +// +// For transactions that only read, snapshot read-only transactions +// provide simpler semantics and are almost always faster. In +// particular, read-only transactions do not take locks, so they do +// not conflict with read-write transactions. As a consequence of not +// taking locks, they also do not abort, so retry loops are not needed. +// +// Transactions may only read-write data in a single database. They +// may, however, read-write data in different tables within that +// database. +// +// Locking read-write transactions: +// +// Locking transactions may be used to atomically read-modify-write +// data anywhere in a database. This type of transaction is externally +// consistent. +// +// Clients should attempt to minimize the amount of time a transaction +// is active. Faster transactions commit with higher probability +// and cause less contention. Cloud Spanner attempts to keep read locks +// active as long as the transaction continues to do reads, and the +// transaction has not been terminated by +// [Commit][google.spanner.v1.Spanner.Commit] or +// [Rollback][google.spanner.v1.Spanner.Rollback]. Long periods of +// inactivity at the client may cause Cloud Spanner to release a +// transaction's locks and abort it. +// +// Conceptually, a read-write transaction consists of zero or more +// reads or SQL statements followed by +// [Commit][google.spanner.v1.Spanner.Commit]. At any time before +// [Commit][google.spanner.v1.Spanner.Commit], the client can send a +// [Rollback][google.spanner.v1.Spanner.Rollback] request to abort the +// transaction. +// +// Semantics: +// +// Cloud Spanner can commit the transaction if all read locks it acquired +// are still valid at commit time, and it is able to acquire write +// locks for all writes. Cloud Spanner can abort the transaction for any +// reason. If a commit attempt returns `ABORTED`, Cloud Spanner guarantees +// that the transaction has not modified any user data in Cloud Spanner. +// +// Unless the transaction commits, Cloud Spanner makes no guarantees about +// how long the transaction's locks were held for. It is an error to +// use Cloud Spanner locks for any sort of mutual exclusion other than +// between Cloud Spanner transactions themselves. +// +// Retrying aborted transactions: +// +// When a transaction aborts, the application can choose to retry the +// whole transaction again. To maximize the chances of successfully +// committing the retry, the client should execute the retry in the +// same session as the original attempt. The original session's lock +// priority increases with each consecutive abort, meaning that each +// attempt has a slightly better chance of success than the previous. +// +// Under some circumstances (for example, many transactions attempting to +// modify the same row(s)), a transaction can abort many times in a +// short period before successfully committing. Thus, it is not a good +// idea to cap the number of retries a transaction can attempt; +// instead, it is better to limit the total amount of time spent +// retrying. +// +// Idle transactions: +// +// A transaction is considered idle if it has no outstanding reads or +// SQL queries and has not started a read or SQL query within the last 10 +// seconds. Idle transactions can be aborted by Cloud Spanner so that they +// don't hold on to locks indefinitely. If an idle transaction is aborted, the +// commit will fail with error `ABORTED`. +// +// If this behavior is undesirable, periodically executing a simple +// SQL query in the transaction (for example, `SELECT 1`) prevents the +// transaction from becoming idle. +// +// Snapshot read-only transactions: +// +// Snapshot read-only transactions provides a simpler method than +// locking read-write transactions for doing several consistent +// reads. However, this type of transaction does not support writes. +// +// Snapshot transactions do not take locks. Instead, they work by +// choosing a Cloud Spanner timestamp, then executing all reads at that +// timestamp. Since they do not acquire locks, they do not block +// concurrent read-write transactions. +// +// Unlike locking read-write transactions, snapshot read-only +// transactions never abort. They can fail if the chosen read +// timestamp is garbage collected; however, the default garbage +// collection policy is generous enough that most applications do not +// need to worry about this in practice. +// +// Snapshot read-only transactions do not need to call +// [Commit][google.spanner.v1.Spanner.Commit] or +// [Rollback][google.spanner.v1.Spanner.Rollback] (and in fact are not +// permitted to do so). +// +// To execute a snapshot transaction, the client specifies a timestamp +// bound, which tells Cloud Spanner how to choose a read timestamp. +// +// The types of timestamp bound are: +// +// - Strong (the default). +// - Bounded staleness. +// - Exact staleness. +// +// If the Cloud Spanner database to be read is geographically distributed, +// stale read-only transactions can execute more quickly than strong +// or read-write transactions, because they are able to execute far +// from the leader replica. +// +// Each type of timestamp bound is discussed in detail below. +// +// Strong: Strong reads are guaranteed to see the effects of all transactions +// that have committed before the start of the read. Furthermore, all +// rows yielded by a single read are consistent with each other -- if +// any part of the read observes a transaction, all parts of the read +// see the transaction. +// +// Strong reads are not repeatable: two consecutive strong read-only +// transactions might return inconsistent results if there are +// concurrent writes. If consistency across reads is required, the +// reads should be executed within a transaction or at an exact read +// timestamp. +// +// Queries on change streams (see below for more details) must also specify +// the strong read timestamp bound. +// +// See +// [TransactionOptions.ReadOnly.strong][google.spanner.v1.TransactionOptions.ReadOnly.strong]. +// +// Exact staleness: +// +// These timestamp bounds execute reads at a user-specified +// timestamp. Reads at a timestamp are guaranteed to see a consistent +// prefix of the global transaction history: they observe +// modifications done by all transactions with a commit timestamp less than or +// equal to the read timestamp, and observe none of the modifications done by +// transactions with a larger commit timestamp. They will block until +// all conflicting transactions that may be assigned commit timestamps +// <= the read timestamp have finished. +// +// The timestamp can either be expressed as an absolute Cloud Spanner commit +// timestamp or a staleness relative to the current time. +// +// These modes do not require a "negotiation phase" to pick a +// timestamp. As a result, they execute slightly faster than the +// equivalent boundedly stale concurrency modes. On the other hand, +// boundedly stale reads usually return fresher results. +// +// See +// [TransactionOptions.ReadOnly.read_timestamp][google.spanner.v1.TransactionOptions.ReadOnly.read_timestamp] +// and +// [TransactionOptions.ReadOnly.exact_staleness][google.spanner.v1.TransactionOptions.ReadOnly.exact_staleness]. +// +// Bounded staleness: +// +// Bounded staleness modes allow Cloud Spanner to pick the read timestamp, +// subject to a user-provided staleness bound. Cloud Spanner chooses the +// newest timestamp within the staleness bound that allows execution +// of the reads at the closest available replica without blocking. +// +// All rows yielded are consistent with each other -- if any part of +// the read observes a transaction, all parts of the read see the +// transaction. Boundedly stale reads are not repeatable: two stale +// reads, even if they use the same staleness bound, can execute at +// different timestamps and thus return inconsistent results. +// +// Boundedly stale reads execute in two phases: the first phase +// negotiates a timestamp among all replicas needed to serve the +// read. In the second phase, reads are executed at the negotiated +// timestamp. +// +// As a result of the two phase execution, bounded staleness reads are +// usually a little slower than comparable exact staleness +// reads. However, they are typically able to return fresher +// results, and are more likely to execute at the closest replica. +// +// Because the timestamp negotiation requires up-front knowledge of +// which rows will be read, it can only be used with single-use +// read-only transactions. +// +// See +// [TransactionOptions.ReadOnly.max_staleness][google.spanner.v1.TransactionOptions.ReadOnly.max_staleness] +// and +// [TransactionOptions.ReadOnly.min_read_timestamp][google.spanner.v1.TransactionOptions.ReadOnly.min_read_timestamp]. +// +// Old read timestamps and garbage collection: +// +// Cloud Spanner continuously garbage collects deleted and overwritten data +// in the background to reclaim storage space. This process is known +// as "version GC". By default, version GC reclaims versions after they +// are one hour old. Because of this, Cloud Spanner cannot perform reads +// at read timestamps more than one hour in the past. This +// restriction also applies to in-progress reads and/or SQL queries whose +// timestamp become too old while executing. Reads and SQL queries with +// too-old read timestamps fail with the error `FAILED_PRECONDITION`. +// +// You can configure and extend the `VERSION_RETENTION_PERIOD` of a +// database up to a period as long as one week, which allows Cloud Spanner +// to perform reads up to one week in the past. +// +// Querying change Streams: +// +// A Change Stream is a schema object that can be configured to watch data +// changes on the entire database, a set of tables, or a set of columns +// in a database. +// +// When a change stream is created, Spanner automatically defines a +// corresponding SQL Table-Valued Function (TVF) that can be used to query +// the change records in the associated change stream using the +// ExecuteStreamingSql API. The name of the TVF for a change stream is +// generated from the name of the change stream: READ_. +// +// All queries on change stream TVFs must be executed using the +// ExecuteStreamingSql API with a single-use read-only transaction with a +// strong read-only timestamp_bound. The change stream TVF allows users to +// specify the start_timestamp and end_timestamp for the time range of +// interest. All change records within the retention period is accessible +// using the strong read-only timestamp_bound. All other TransactionOptions +// are invalid for change stream queries. +// +// In addition, if TransactionOptions.read_only.return_read_timestamp is set +// to true, a special value of 2^63 - 2 will be returned in the +// [Transaction][google.spanner.v1.Transaction] message that describes the +// transaction, instead of a valid read timestamp. This special value should be +// discarded and not used for any subsequent queries. +// +// Please see https://cloud.google.com/spanner/docs/change-streams +// for more details on how to query the change stream TVFs. +// +// Partitioned DML transactions: +// +// Partitioned DML transactions are used to execute DML statements with a +// different execution strategy that provides different, and often better, +// scalability properties for large, table-wide operations than DML in a +// ReadWrite transaction. Smaller scoped statements, such as an OLTP workload, +// should prefer using ReadWrite transactions. +// +// Partitioned DML partitions the keyspace and runs the DML statement on each +// partition in separate, internal transactions. These transactions commit +// automatically when complete, and run independently from one another. +// +// To reduce lock contention, this execution strategy only acquires read locks +// on rows that match the WHERE clause of the statement. Additionally, the +// smaller per-partition transactions hold locks for less time. +// +// That said, Partitioned DML is not a drop-in replacement for standard DML used +// in ReadWrite transactions. +// +// - The DML statement must be fully-partitionable. Specifically, the statement +// must be expressible as the union of many statements which each access only +// a single row of the table. +// +// - The statement is not applied atomically to all rows of the table. Rather, +// the statement is applied atomically to partitions of the table, in +// independent transactions. Secondary index rows are updated atomically +// with the base table rows. +// +// - Partitioned DML does not guarantee exactly-once execution semantics +// against a partition. The statement will be applied at least once to each +// partition. It is strongly recommended that the DML statement should be +// idempotent to avoid unexpected results. For instance, it is potentially +// dangerous to run a statement such as +// `UPDATE table SET column = column + 1` as it could be run multiple times +// against some rows. +// +// - The partitions are committed automatically - there is no support for +// Commit or Rollback. If the call returns an error, or if the client issuing +// the ExecuteSql call dies, it is possible that some rows had the statement +// executed on them successfully. It is also possible that statement was +// never executed against other rows. +// +// - Partitioned DML transactions may only contain the execution of a single +// DML statement via ExecuteSql or ExecuteStreamingSql. +// +// - If any error is encountered during the execution of the partitioned DML +// operation (for instance, a UNIQUE INDEX violation, division by zero, or a +// value that cannot be stored due to schema constraints), then the +// operation is stopped at that point and an error is returned. It is +// possible that at this point, some partitions have been committed (or even +// committed multiple times), and other partitions have not been run at all. +// +// Given the above, Partitioned DML is good fit for large, database-wide, +// operations that are idempotent, such as deleting old rows from a very large +// table. +message TransactionOptions { + // Message type to initiate a read-write transaction. Currently this + // transaction type has no options. + message ReadWrite { + // `ReadLockMode` is used to set the read lock mode for read-write + // transactions. + enum ReadLockMode { + // Default value. + // + // * If isolation level is `REPEATABLE_READ`, then it is an error to + // specify `read_lock_mode`. Locking semantics default to `OPTIMISTIC`. + // No validation checks are done for reads, except for: + // 1. reads done as part of queries that use `SELECT FOR UPDATE` + // 2. reads done as part of statements with a `LOCK_SCANNED_RANGES` + // hint + // 3. reads done as part of DML statements + // to validate that the data that was served at the snapshot time is + // unchanged at commit time. + // * At all other isolation levels, if `read_lock_mode` is the default + // value, then pessimistic read lock is used. + READ_LOCK_MODE_UNSPECIFIED = 0; + + // Pessimistic lock mode. + // + // Read locks are acquired immediately on read. + // Semantics described only applies to `SERIALIZABLE` isolation. + PESSIMISTIC = 1; + + // Optimistic lock mode. + // + // Locks for reads within the transaction are not acquired on read. + // Instead the locks are acquired on a commit to validate that + // read/queried data has not changed since the transaction started. + // Semantics described only applies to `SERIALIZABLE` isolation. + OPTIMISTIC = 2; + } + + // Read lock mode for the transaction. + ReadLockMode read_lock_mode = 1; + + // Optional. Clients should pass the transaction ID of the previous + // transaction attempt that was aborted if this transaction is being + // executed on a multiplexed session. + // This feature is not yet supported and will result in an UNIMPLEMENTED + // error. + bytes multiplexed_session_previous_transaction_id = 2 + [(google.api.field_behavior) = OPTIONAL]; + } + + // Message type to initiate a Partitioned DML transaction. + message PartitionedDml {} + + // Message type to initiate a read-only transaction. + message ReadOnly { + // How to choose the timestamp for the read-only transaction. + oneof timestamp_bound { + // Read at a timestamp where all previously committed transactions + // are visible. + bool strong = 1; + + // Executes all reads at a timestamp >= `min_read_timestamp`. + // + // This is useful for requesting fresher data than some previous + // read, or data that is fresh enough to observe the effects of some + // previously committed transaction whose timestamp is known. + // + // Note that this option can only be used in single-use transactions. + // + // A timestamp in RFC3339 UTC \"Zulu\" format, accurate to nanoseconds. + // Example: `"2014-10-02T15:01:23.045123456Z"`. + google.protobuf.Timestamp min_read_timestamp = 2; + + // Read data at a timestamp >= `NOW - max_staleness` + // seconds. Guarantees that all writes that have committed more + // than the specified number of seconds ago are visible. Because + // Cloud Spanner chooses the exact timestamp, this mode works even if + // the client's local clock is substantially skewed from Cloud Spanner + // commit timestamps. + // + // Useful for reading the freshest data available at a nearby + // replica, while bounding the possible staleness if the local + // replica has fallen behind. + // + // Note that this option can only be used in single-use + // transactions. + google.protobuf.Duration max_staleness = 3; + + // Executes all reads at the given timestamp. Unlike other modes, + // reads at a specific timestamp are repeatable; the same read at + // the same timestamp always returns the same data. If the + // timestamp is in the future, the read will block until the + // specified timestamp, modulo the read's deadline. + // + // Useful for large scale consistent reads such as mapreduces, or + // for coordinating many reads against a consistent snapshot of the + // data. + // + // A timestamp in RFC3339 UTC \"Zulu\" format, accurate to nanoseconds. + // Example: `"2014-10-02T15:01:23.045123456Z"`. + google.protobuf.Timestamp read_timestamp = 4; + + // Executes all reads at a timestamp that is `exact_staleness` + // old. The timestamp is chosen soon after the read is started. + // + // Guarantees that all writes that have committed more than the + // specified number of seconds ago are visible. Because Cloud Spanner + // chooses the exact timestamp, this mode works even if the client's + // local clock is substantially skewed from Cloud Spanner commit + // timestamps. + // + // Useful for reading at nearby replicas without the distributed + // timestamp negotiation overhead of `max_staleness`. + google.protobuf.Duration exact_staleness = 5; + } + + // If true, the Cloud Spanner-selected read timestamp is included in + // the [Transaction][google.spanner.v1.Transaction] message that describes + // the transaction. + bool return_read_timestamp = 6; + } + + // `IsolationLevel` is used when setting `isolation_level` for a transaction. + enum IsolationLevel { + // Default value. + // + // If the value is not specified, the `SERIALIZABLE` isolation level is + // used. + ISOLATION_LEVEL_UNSPECIFIED = 0; + + // All transactions appear as if they executed in a serial order, even if + // some of the reads, writes, and other operations of distinct transactions + // actually occurred in parallel. Spanner assigns commit timestamps that + // reflect the order of committed transactions to implement this property. + // Spanner offers a stronger guarantee than serializability called external + // consistency. For further details, please refer to + // https://cloud.google.com/spanner/docs/true-time-external-consistency#serializability. + SERIALIZABLE = 1; + + // All reads performed during the transaction observe a consistent snapshot + // of the database, and the transaction will only successfully commit in the + // absence of conflicts between its updates and any concurrent updates that + // have occurred since that snapshot. Consequently, in contrast to + // `SERIALIZABLE` transactions, only write-write conflicts are detected in + // snapshot transactions. + // + // This isolation level does not support Read-only and Partitioned DML + // transactions. + // + // When `REPEATABLE_READ` is specified on a read-write transaction, the + // locking semantics default to `OPTIMISTIC`. + REPEATABLE_READ = 2; + } + + // Required. The type of transaction. + oneof mode { + // Transaction may write. + // + // Authorization to begin a read-write transaction requires + // `spanner.databases.beginOrRollbackReadWriteTransaction` permission + // on the `session` resource. + ReadWrite read_write = 1; + + // Partitioned DML transaction. + // + // Authorization to begin a Partitioned DML transaction requires + // `spanner.databases.beginPartitionedDmlTransaction` permission + // on the `session` resource. + PartitionedDml partitioned_dml = 3; + + // Transaction will not write. + // + // Authorization to begin a read-only transaction requires + // `spanner.databases.beginReadOnlyTransaction` permission + // on the `session` resource. + ReadOnly read_only = 2; + } + + // When `exclude_txn_from_change_streams` is set to `true`: + // * Mutations from this transaction will not be recorded in change streams + // with DDL option `allow_txn_exclusion=true` that are tracking columns + // modified by these transactions. + // * Mutations from this transaction will be recorded in change streams with + // DDL option `allow_txn_exclusion=false or not set` that are tracking + // columns modified by these transactions. + // + // When `exclude_txn_from_change_streams` is set to `false` or not set, + // mutations from this transaction will be recorded in all change streams that + // are tracking columns modified by these transactions. + // `exclude_txn_from_change_streams` may only be specified for read-write or + // partitioned-dml transactions, otherwise the API will return an + // `INVALID_ARGUMENT` error. + bool exclude_txn_from_change_streams = 5; + + // Isolation level for the transaction. + IsolationLevel isolation_level = 6; +} + +// A transaction. +message Transaction { + // `id` may be used to identify the transaction in subsequent + // [Read][google.spanner.v1.Spanner.Read], + // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], + // [Commit][google.spanner.v1.Spanner.Commit], or + // [Rollback][google.spanner.v1.Spanner.Rollback] calls. + // + // Single-use read-only transactions do not have IDs, because + // single-use transactions do not support multiple requests. + bytes id = 1; + + // For snapshot read-only transactions, the read timestamp chosen + // for the transaction. Not returned by default: see + // [TransactionOptions.ReadOnly.return_read_timestamp][google.spanner.v1.TransactionOptions.ReadOnly.return_read_timestamp]. + // + // A timestamp in RFC3339 UTC \"Zulu\" format, accurate to nanoseconds. + // Example: `"2014-10-02T15:01:23.045123456Z"`. + google.protobuf.Timestamp read_timestamp = 2; + + // A precommit token will be included in the response of a BeginTransaction + // request if the read-write transaction is on a multiplexed session and + // a mutation_key was specified in the + // [BeginTransaction][google.spanner.v1.BeginTransactionRequest]. + // The precommit token with the highest sequence number from this transaction + // attempt should be passed to the [Commit][google.spanner.v1.Spanner.Commit] + // request for this transaction. + // This feature is not yet supported and will result in an UNIMPLEMENTED + // error. + MultiplexedSessionPrecommitToken precommit_token = 3; +} + +// This message is used to select the transaction in which a +// [Read][google.spanner.v1.Spanner.Read] or +// [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] call runs. +// +// See [TransactionOptions][google.spanner.v1.TransactionOptions] for more +// information about transactions. +message TransactionSelector { + // If no fields are set, the default is a single use transaction + // with strong concurrency. + oneof selector { + // Execute the read or SQL query in a temporary transaction. + // This is the most efficient way to execute a transaction that + // consists of a single SQL query. + TransactionOptions single_use = 1; + + // Execute the read or SQL query in a previously-started transaction. + bytes id = 2; + + // Begin a new transaction and execute this read or SQL query in + // it. The transaction ID of the new transaction is returned in + // [ResultSetMetadata.transaction][google.spanner.v1.ResultSetMetadata.transaction], + // which is a [Transaction][google.spanner.v1.Transaction]. + TransactionOptions begin = 3; + } +} + +// When a read-write transaction is executed on a multiplexed session, +// this precommit token is sent back to the client +// as a part of the [Transaction] message in the BeginTransaction response and +// also as a part of the [ResultSet] and [PartialResultSet] responses. +message MultiplexedSessionPrecommitToken { + // Opaque precommit token. + bytes precommit_token = 1; + + // An incrementing seq number is generated on every precommit token + // that is returned. Clients should remember the precommit token with the + // highest sequence number from the current transaction attempt. + int32 seq_num = 2; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/type.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/type.proto new file mode 100644 index 00000000..3e01729f --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spanner/v1/type.proto @@ -0,0 +1,214 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.spanner.v1; + +import "google/api/field_behavior.proto"; + +option csharp_namespace = "Google.Cloud.Spanner.V1"; +option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb"; +option java_multiple_files = true; +option java_outer_classname = "TypeProto"; +option java_package = "com.google.spanner.v1"; +option php_namespace = "Google\\Cloud\\Spanner\\V1"; +option ruby_package = "Google::Cloud::Spanner::V1"; + +// `Type` indicates the type of a Cloud Spanner value, as might be stored in a +// table cell or returned from an SQL query. +message Type { + // Required. The [TypeCode][google.spanner.v1.TypeCode] for this type. + TypeCode code = 1 [(google.api.field_behavior) = REQUIRED]; + + // If [code][google.spanner.v1.Type.code] == + // [ARRAY][google.spanner.v1.TypeCode.ARRAY], then `array_element_type` is the + // type of the array elements. + Type array_element_type = 2; + + // If [code][google.spanner.v1.Type.code] == + // [STRUCT][google.spanner.v1.TypeCode.STRUCT], then `struct_type` provides + // type information for the struct's fields. + StructType struct_type = 3; + + // The [TypeAnnotationCode][google.spanner.v1.TypeAnnotationCode] that + // disambiguates SQL type that Spanner will use to represent values of this + // type during query processing. This is necessary for some type codes because + // a single [TypeCode][google.spanner.v1.TypeCode] can be mapped to different + // SQL types depending on the SQL dialect. + // [type_annotation][google.spanner.v1.Type.type_annotation] typically is not + // needed to process the content of a value (it doesn't affect serialization) + // and clients can ignore it on the read path. + TypeAnnotationCode type_annotation = 4; + + // If [code][google.spanner.v1.Type.code] == + // [PROTO][google.spanner.v1.TypeCode.PROTO] or + // [code][google.spanner.v1.Type.code] == + // [ENUM][google.spanner.v1.TypeCode.ENUM], then `proto_type_fqn` is the fully + // qualified name of the proto type representing the proto/enum definition. + string proto_type_fqn = 5; +} + +// `StructType` defines the fields of a +// [STRUCT][google.spanner.v1.TypeCode.STRUCT] type. +message StructType { + // Message representing a single field of a struct. + message Field { + // The name of the field. For reads, this is the column name. For + // SQL queries, it is the column alias (e.g., `"Word"` in the + // query `"SELECT 'hello' AS Word"`), or the column name (e.g., + // `"ColName"` in the query `"SELECT ColName FROM Table"`). Some + // columns might have an empty name (e.g., `"SELECT + // UPPER(ColName)"`). Note that a query result can contain + // multiple fields with the same name. + string name = 1; + + // The type of the field. + Type type = 2; + } + + // The list of fields that make up this struct. Order is + // significant, because values of this struct type are represented as + // lists, where the order of field values matches the order of + // fields in the [StructType][google.spanner.v1.StructType]. In turn, the + // order of fields matches the order of columns in a read request, or the + // order of fields in the `SELECT` clause of a query. + repeated Field fields = 1; +} + +// `TypeCode` is used as part of [Type][google.spanner.v1.Type] to +// indicate the type of a Cloud Spanner value. +// +// Each legal value of a type can be encoded to or decoded from a JSON +// value, using the encodings described below. All Cloud Spanner values can +// be `null`, regardless of type; `null`s are always encoded as a JSON +// `null`. +enum TypeCode { + // Not specified. + TYPE_CODE_UNSPECIFIED = 0; + + // Encoded as JSON `true` or `false`. + BOOL = 1; + + // Encoded as `string`, in decimal format. + INT64 = 2; + + // Encoded as `number`, or the strings `"NaN"`, `"Infinity"`, or + // `"-Infinity"`. + FLOAT64 = 3; + + // Encoded as `number`, or the strings `"NaN"`, `"Infinity"`, or + // `"-Infinity"`. + FLOAT32 = 15; + + // Encoded as `string` in RFC 3339 timestamp format. The time zone + // must be present, and must be `"Z"`. + // + // If the schema has the column option + // `allow_commit_timestamp=true`, the placeholder string + // `"spanner.commit_timestamp()"` can be used to instruct the system + // to insert the commit timestamp associated with the transaction + // commit. + TIMESTAMP = 4; + + // Encoded as `string` in RFC 3339 date format. + DATE = 5; + + // Encoded as `string`. + STRING = 6; + + // Encoded as a base64-encoded `string`, as described in RFC 4648, + // section 4. + BYTES = 7; + + // Encoded as `list`, where the list elements are represented + // according to + // [array_element_type][google.spanner.v1.Type.array_element_type]. + ARRAY = 8; + + // Encoded as `list`, where list element `i` is represented according + // to [struct_type.fields[i]][google.spanner.v1.StructType.fields]. + STRUCT = 9; + + // Encoded as `string`, in decimal format or scientific notation format. + // Decimal format: + // `[+-]Digits[.[Digits]]` or + // `[+-][Digits].Digits` + // + // Scientific notation: + // `[+-]Digits[.[Digits]][ExponentIndicator[+-]Digits]` or + // `[+-][Digits].Digits[ExponentIndicator[+-]Digits]` + // (ExponentIndicator is `"e"` or `"E"`) + NUMERIC = 10; + + // Encoded as a JSON-formatted `string` as described in RFC 7159. The + // following rules are applied when parsing JSON input: + // + // - Whitespace characters are not preserved. + // - If a JSON object has duplicate keys, only the first key is preserved. + // - Members of a JSON object are not guaranteed to have their order + // preserved. + // - JSON array elements will have their order preserved. + JSON = 11; + + // Encoded as a base64-encoded `string`, as described in RFC 4648, + // section 4. + PROTO = 13; + + // Encoded as `string`, in decimal format. + ENUM = 14; + + // Encoded as `string`, in `ISO8601` duration format - + // `P[n]Y[n]M[n]DT[n]H[n]M[n[.fraction]]S` + // where `n` is an integer. + // For example, `P1Y2M3DT4H5M6.5S` represents time duration of 1 year, 2 + // months, 3 days, 4 hours, 5 minutes, and 6.5 seconds. + INTERVAL = 16; + + // Encoded as `string`, in lower-case hexa-decimal format, as described + // in RFC 9562, section 4. + UUID = 17; +} + +// `TypeAnnotationCode` is used as a part of [Type][google.spanner.v1.Type] to +// disambiguate SQL types that should be used for a given Cloud Spanner value. +// Disambiguation is needed because the same Cloud Spanner type can be mapped to +// different SQL types depending on SQL dialect. TypeAnnotationCode doesn't +// affect the way value is serialized. +enum TypeAnnotationCode { + // Not specified. + TYPE_ANNOTATION_CODE_UNSPECIFIED = 0; + + // PostgreSQL compatible NUMERIC type. This annotation needs to be applied to + // [Type][google.spanner.v1.Type] instances having + // [NUMERIC][google.spanner.v1.TypeCode.NUMERIC] type code to specify that + // values of this type should be treated as PostgreSQL NUMERIC values. + // Currently this annotation is always needed for + // [NUMERIC][google.spanner.v1.TypeCode.NUMERIC] when a client interacts with + // PostgreSQL-enabled Spanner databases. + PG_NUMERIC = 2; + + // PostgreSQL compatible JSONB type. This annotation needs to be applied to + // [Type][google.spanner.v1.Type] instances having + // [JSON][google.spanner.v1.TypeCode.JSON] type code to specify that values of + // this type should be treated as PostgreSQL JSONB values. Currently this + // annotation is always needed for [JSON][google.spanner.v1.TypeCode.JSON] + // when a client interacts with PostgreSQL-enabled Spanner databases. + PG_JSONB = 3; + + // PostgreSQL compatible OID type. This annotation can be used by a client + // interacting with PostgreSQL-enabled Spanner database to specify that a + // value should be treated using the semantics of the OID type. + PG_OID = 4; +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spannerlib/v1/spannerlib.proto b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spannerlib/v1/spannerlib.proto new file mode 100644 index 00000000..aba470c2 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Grpc/google/spannerlib/v1/spannerlib.proto @@ -0,0 +1,174 @@ +syntax = "proto3"; + +package google.spannerlib.v1; + +import "google/api/field_behavior.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/struct.proto"; +import "google/spanner/v1/mutation.proto"; +import "google/spanner/v1/result_set.proto"; +import "google/spanner/v1/spanner.proto"; +import "google/spanner/v1/transaction.proto"; + +option csharp_namespace = "Google.Cloud.SpannerLib.V1"; +option go_package = "cloud.google.com/go/spannerlib/apiv1/spannerlibpb;spannerlibpb"; +option java_multiple_files = true; +option java_outer_classname = "SpannerLibProto"; +option java_package = "com.google.spannerlib.v1"; +option php_namespace = "Google\\Cloud\\SpannerLib\\V1"; +option ruby_package = "Google::Cloud::SpannerLib::V1"; + +service SpannerLib { + rpc CreatePool(CreatePoolRequest) returns (Pool) {} + rpc ClosePool(Pool) returns (google.protobuf.Empty) {} + + rpc CreateConnection(CreateConnectionRequest) returns (Connection) {} + rpc CloseConnection(Connection) returns (google.protobuf.Empty) {} + + rpc Execute(ExecuteRequest) returns (Rows) {} + rpc ExecuteStreaming(ExecuteRequest) returns (stream google.spanner.v1.PartialResultSet) {} + rpc ExecuteTransaction(ExecuteTransactionRequest) returns (Rows) {} + rpc ExecuteBatchDml(ExecuteBatchDmlRequest) returns (google.spanner.v1.ExecuteBatchDmlResponse) {} + rpc Metadata(Rows) returns (google.spanner.v1.ResultSetMetadata) {} + rpc Next(Rows) returns (google.protobuf.ListValue) {} + rpc ResultSetStats(Rows) returns (google.spanner.v1.ResultSetStats) {} + rpc CloseRows(Rows) returns (google.protobuf.Empty) {} + + rpc BeginTransaction(BeginTransactionRequest) returns (Transaction) {} + rpc Commit(Transaction) returns (google.spanner.v1.CommitResponse) {} + rpc Rollback(Transaction) returns (google.protobuf.Empty) {} + + rpc Apply(ApplyRequest) returns (google.spanner.v1.CommitResponse) {} + rpc BufferWrite(BufferWriteRequest) returns (google.protobuf.Empty) {} + + rpc ConnectionStream(stream ConnectionStreamRequest) returns (stream ConnectionStreamResponse) {} +} + +message CreatePoolRequest { + string dsn = 1 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message CreateConnectionRequest { + Pool pool = 1 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message ExecuteRequest { + Connection connection = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + google.spanner.v1.ExecuteSqlRequest execute_sql_request = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message ExecuteTransactionRequest { + Transaction transaction = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + google.spanner.v1.ExecuteSqlRequest execute_sql_request = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message ExecuteBatchDmlRequest { + Connection connection = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + google.spanner.v1.ExecuteBatchDmlRequest execute_batch_dml_request = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message BeginTransactionRequest { + Connection connection = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + google.spanner.v1.TransactionOptions transaction_options = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message ApplyRequest { + Connection connection = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + google.spanner.v1.BatchWriteRequest.MutationGroup mutations = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message BufferWriteRequest { + Transaction transaction = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + google.spanner.v1.BatchWriteRequest.MutationGroup mutations = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message Pool { + int64 id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message Connection { + Pool pool = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + int64 id = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message Rows { + Connection connection = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + int64 id = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message NextRequest { + Rows rows = 1 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message MetadataRequest { + Rows rows = 1 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message ResultSetStatsRequest { + Rows rows = 1 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message Transaction { + Connection connection = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + int64 id = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message ConnectionStreamRequest { + oneof request { + ExecuteRequest execute_request = 1; + } +} + +message ConnectionStreamResponse { + oneof response { + google.spanner.v1.PartialResultSet row = 1; + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.GrpcTests/Google.Cloud.SpannerLib.GrpcTests.csproj b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.GrpcTests/Google.Cloud.SpannerLib.GrpcTests.csproj new file mode 100644 index 00000000..2c81206c --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.GrpcTests/Google.Cloud.SpannerLib.GrpcTests.csproj @@ -0,0 +1,24 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.GrpcTests/UnitTest1.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.GrpcTests/UnitTest1.cs new file mode 100644 index 00000000..561e3e37 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.GrpcTests/UnitTest1.cs @@ -0,0 +1,130 @@ +using Google.Cloud.Spanner.V1; +using Google.Cloud.SpannerLib.V1; +using Grpc.Core; + +namespace Google.Cloud.SpannerLib.GrpcTests; + +public class Tests +{ + [SetUp] + public void Setup() + { + } + + [Test] + public async Task TestStreaming() + { + var lib = new Grpc.SpannerLib(); + var pool = lib.CreatePool( + "projects/appdev-soda-spanner-staging/instances/knut-test-ycsb/databases/knut-test-db"); + var connection = lib.CreateConnection(pool); + + var stream = + lib.ExecuteStreaming(connection, new ExecuteSqlRequest { Sql = "select * from all_types limit 10" }); + var first = true; + await foreach (var result in stream.ResponseStream.ReadAllAsync()) + { + if (first) + { + first = false; + foreach (var field in result.Metadata.RowType.Fields) + { + Console.Write(field.Name); + Console.Write("|"); + } + Console.WriteLine(); + } + foreach (var row in result.Rows) + { + foreach (var value in row.Values) + { + Console.Write(value.ToString()); + Console.Write("|"); + } + } + Console.WriteLine(); + } + lib.CloseConnection(connection); + lib.ClosePool(pool); + } + + [Test] + [Ignore("Intended for local testing")] + public async Task Test1() + { + var lib = new Grpc.SpannerLib(); + var pool = lib.CreatePool( + "projects/appdev-soda-spanner-staging/instances/knut-test-ycsb/databases/knut-test-db"); + var connection = lib.CreateConnection(pool); + + var rows = lib.Execute(connection, new ExecuteSqlRequest{Sql = "select * from all_types limit 10"}); + var metadata = lib.Metadata(rows); + foreach (var field in metadata.RowType.Fields) + { + Console.Write(field.Name); + Console.Write("|"); + } + Console.WriteLine(); + for (var row = lib.Next(rows); row.Values.Count > 0; row = lib.Next(rows)) + { + foreach (var value in row.Values) + { + Console.Write(value.ToString()); + Console.Write("|"); + } + Console.WriteLine(); + } + lib.CloseRows(rows); + + await TestStream(lib, pool, connection); + + lib.CloseConnection(connection); + lib.ClosePool(pool); + } + + private async Task TestStream(Grpc.SpannerLib lib, Pool pool, Connection connection) + { + Console.WriteLine(); + Console.WriteLine("Starting stream"); + using var stream = lib.CreateStream(); + await stream.RequestStream.WriteAsync(new ConnectionStreamRequest + { + ExecuteRequest = new ExecuteRequest + { + Connection = connection, + ExecuteSqlRequest = new ExecuteSqlRequest{Sql = "select * from all_types limit 10"}, + } + }); + await stream.ResponseStream.MoveNext(CancellationToken.None); + var response = stream.ResponseStream.Current; + if (response.ResponseCase == ConnectionStreamResponse.ResponseOneofCase.Rows) + { + var rows = response.Rows; + while (true) + { + await stream.RequestStream.WriteAsync(new ConnectionStreamRequest + { + NextRequest = new NextRequest{Rows = rows} + }); + await stream.ResponseStream.MoveNext(CancellationToken.None); + var rowResponse = stream.ResponseStream.Current; + if (rowResponse.ResponseCase == ConnectionStreamResponse.ResponseOneofCase.Row) + { + var row = rowResponse.Row; + if (row.Values.Count > 0) + { + Console.Write(row.Values.ToString()); + } + else + { + break; + } + } + else + { + Console.WriteLine("Unexpected response: " + response.ResponseCase); + } + } + } + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/Google.Cloud.SpannerLib.MockServer.csproj b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/Google.Cloud.SpannerLib.MockServer.csproj new file mode 100644 index 00000000..f085be9c --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/Google.Cloud.SpannerLib.MockServer.csproj @@ -0,0 +1,15 @@ + + + + net9.0 + enable + default + + + + + + + + + diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/MockServerStartup.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/MockServerStartup.cs new file mode 100644 index 00000000..5d10d429 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/MockServerStartup.cs @@ -0,0 +1,75 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Google.Cloud.SpannerLib.Tests.MockServer; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Google.Cloud.SpannerLib.MockServer; + +/// +/// Helper class for starting an in-memory mock Spanner server that is used for testing. +/// +public class MockServerStartup +{ + /// + /// The in-mem Spanner service. + /// + private MockSpannerService MockSpannerService { get; } + + /// + /// The in-mem Spanner database admin service for executing DDL operations. + /// + private MockDatabaseAdminService MockDatabaseAdminService { get; } + + public MockServerStartup(MockSpannerService mockSpannerService, MockDatabaseAdminService mockDatabaseAdminService) + { + MockSpannerService = mockSpannerService; + MockDatabaseAdminService = mockDatabaseAdminService; + } + + /// + /// Configures the services that will be available on this gRPC server. + /// This method is called by reflection when the tests are started. + /// + /// The services collection where the services should be added + public void ConfigureServices(IServiceCollection services) + { + services.AddGrpc(); + services.AddSingleton(MockSpannerService); + services.AddSingleton(MockDatabaseAdminService); + } + + /// + /// Configures the gRPC server. This method is called by reflection when the tests are started. + /// + /// The builder for the application that will be hosting the service + /// The webhost environment that is hosting the service + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + endpoints.MapGrpcService(); + }); + } +} + \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/MockSpannerServer.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/MockSpannerServer.cs new file mode 100644 index 00000000..695d7957 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/MockSpannerServer.cs @@ -0,0 +1,789 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Google.Cloud.Spanner.Admin.Database.V1; +using Google.Cloud.Spanner.Common.V1; +using Google.Cloud.Spanner.V1; +using Google.Cloud.SpannerLib.Tests.MockServer; +using Google.LongRunning; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Google.Rpc; +using Grpc.Core; +using Status = Google.Rpc.Status; +using GrpcCore = Grpc.Core; + +namespace Google.Cloud.SpannerLib.MockServer +{ + public class StatementResult + { + public enum StatementResultType + { + ResultSet, + UpdateCount, + Exception + } + + public StatementResultType Type { get; } + public ResultSet ResultSet { get; } + public long UpdateCount { get; } + public Exception Exception { get; } + + public static StatementResult CreateQuery(ResultSet resultSet) + { + return new StatementResult(resultSet); + } + + public static StatementResult CreateUpdateCount(long count) + { + return new StatementResult(count); + } + + public static StatementResult CreateException(Exception exception) + { + return new StatementResult(exception); + } + + public static StatementResult CreateSelect1ResultSet() + { + return CreateSingleColumnResultSet(new Spanner.V1.Type { Code = Spanner.V1.TypeCode.Int64 }, "COL1", 1); + } + + public static StatementResult CreateSingleColumnResultSet(Spanner.V1.Type type, string col, params object[] values) + => CreateSingleColumnResultSet(null, type, col, values); + + public static StatementResult CreateSingleColumnResultSet(long? updateCount, Spanner.V1.Type type, string col, params object[] values) + { + ResultSet rs = new ResultSet + { + Metadata = new ResultSetMetadata + { + RowType = new StructType() + }, + }; + rs.Metadata.RowType.Fields.Add(new StructType.Types.Field + { + Name = col, + Type = type, + }); + foreach (object val in values) + { + ListValue row = new ListValue(); + row.Values.Add(SpannerConverter.ToProtobufValue(type, val)); + rs.Rows.Add(row); + } + if (updateCount != null) + { + rs.Stats = new ResultSetStats { RowCountExact = updateCount.Value }; + } + return CreateQuery(rs); + } + + public static StatementResult CreateResultSet(IEnumerable> columns, IEnumerable rows) => + CreateResultSet(columns.Select(x => Tuple.Create(new Spanner.V1.Type { Code = x.Item1, ArrayElementType = x.Item1 == Spanner.V1.TypeCode.Array ? new Spanner.V1.Type {Code=Spanner.V1.TypeCode.String} : null}, x.Item2)).ToList(), rows); + + public static StatementResult CreateResultSet(IEnumerable> columns, IEnumerable rows) + { + var rs = new ResultSet + { + Metadata = new ResultSetMetadata + { + RowType = new StructType() + }, + }; + foreach (var col in columns) + { + rs.Metadata.RowType.Fields.Add(new StructType.Types.Field + { + Type = col.Item1, + Name = col.Item2, + }); + } + foreach (var rowValue in rows) + { + var row = new ListValue(); + var colIndex = 0; + foreach (var value in rowValue) + { + row.Values.Add(SpannerConverter.ToProtobufValue(rs.Metadata.RowType.Fields[colIndex].Type, value)); + colIndex++; + } + rs.Rows.Add(row); + } + return CreateQuery(rs); + } + + private StatementResult(ResultSet resultSet) + { + Type = StatementResultType.ResultSet; + ResultSet = resultSet; + } + + private StatementResult(long updateCount) + { + Type = StatementResultType.UpdateCount; + UpdateCount = updateCount; + } + + private StatementResult(Exception exception) + { + Type = StatementResultType.Exception; + Exception = exception; + } + } + + public class ExecutionTime + { + private readonly int _minimumExecutionTime; + private readonly int _randomExecutionTime; + private readonly Random _random; + + // TODO: Support multiple exceptions + private Exception _exception; + private readonly int _exceptionStreamIndex; + private readonly BlockingCollection _streamWritePermissions; + private bool _alwaysAllowWrite; + + internal bool HasExceptionAtIndex(int index) + { + return _exception != null && _exceptionStreamIndex == index; + } + + internal Exception PopExceptionAtIndex(int index) + { + Exception res = _exceptionStreamIndex == index ? _exception : null; + if (res != null) + { + _exception = null; + } + return res; + } + + public void AlwaysAllowWrite() + { + _alwaysAllowWrite = true; + _streamWritePermissions.Add(int.MaxValue); + } + + internal int TakeWritePermission() + { + if (_alwaysAllowWrite || _streamWritePermissions == null) + { + return int.MaxValue; + } + return _streamWritePermissions.Take(); + } + + internal void SimulateExecutionTime() + { + if (_minimumExecutionTime > 0 || _randomExecutionTime > 0) + { + int totalWaitTime = _minimumExecutionTime; + if (_randomExecutionTime > 0) + { + totalWaitTime += _random.Next(_randomExecutionTime); + } + Thread.Sleep(totalWaitTime); + } + } + + public static ExecutionTime FromMillis(int minimumExecutionTime, int randomExecutionTime) + { + return new ExecutionTime(minimumExecutionTime, randomExecutionTime, null, -1, null); + } + + internal static ExecutionTime StreamException(Exception exception, int streamIndex, BlockingCollection streamWritePermissions) + { + return new ExecutionTime(0, 0, exception, streamIndex, streamWritePermissions); + } + + private ExecutionTime(int minimumExecutionTime, int randomExecutionTime, Exception exception, int exceptionStreamIndex, BlockingCollection streamWritePermissions) + { + _minimumExecutionTime = minimumExecutionTime; + _randomExecutionTime = randomExecutionTime; + _random = _randomExecutionTime > 0 ? new Random() : null; + _exception = exception; + _exceptionStreamIndex = exceptionStreamIndex; + _streamWritePermissions = streamWritePermissions; + } + } + + public class MockSpannerService : Spanner.V1.Spanner.SpannerBase + { + private class PartialResultSetsEnumerable : IEnumerable + { + private readonly Spanner.V1.Transaction _transaction; + private readonly ResultSet _resultSet; + public PartialResultSetsEnumerable(Spanner.V1.Transaction transaction, ResultSet resultSet) + { + _transaction = transaction; + _resultSet = resultSet; + } + + public IEnumerator GetEnumerator() + { + return new PartialResultSetsEnumerator(_transaction, _resultSet); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new PartialResultSetsEnumerator(_transaction, _resultSet); + } + } + + private class PartialResultSetsEnumerator : IEnumerator + { + private static readonly int s_maxRowsInChunk = 1; + + private readonly Spanner.V1.Transaction _transaction; + private readonly ResultSet _resultSet; + private bool _first = true; + private int _currentRow; + private PartialResultSet _current; + + public PartialResultSetsEnumerator(Spanner.V1.Transaction transaction, ResultSet resultSet) + { + _transaction = transaction; + _resultSet = resultSet; + } + + PartialResultSet IEnumerator.Current => _current; + + object IEnumerator.Current => _current; + + public bool MoveNext() + { + _current = new PartialResultSet + { + ResumeToken = ByteString.CopyFromUtf8($"{_currentRow}") + }; + if (_first) + { + _current.Metadata = _resultSet.Metadata.Clone(); + if (_transaction != null) + { + _current.Metadata.Transaction = _transaction; + } + _first = false; + } + else if (_currentRow == _resultSet.Rows.Count) + { + return false; + } + int recordCount = 0; + while (recordCount < s_maxRowsInChunk && _currentRow < _resultSet.Rows.Count) + { + _current.Values.Add(_resultSet.Rows.ElementAt(_currentRow).Values); + recordCount++; + _currentRow++; + } + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + public void Dispose() + { + } + } + + private static readonly string s_dialect_query = + "select option_value from information_schema.database_options where option_name='database_dialect'"; + private static readonly Empty s_empty = new (); + private static readonly TransactionOptions s_singleUse = new() { ReadOnly = new TransactionOptions.Types.ReadOnly { Strong = true, ReturnReadTimestamp = false } }; + + private readonly object _lock = new(); + private readonly ConcurrentDictionary _results = new(); + private ConcurrentQueue _requests = new(); + private ConcurrentQueue _contexts = new(); + private ConcurrentQueue _headers = new (); + private int _sessionCounter; + private int _transactionCounter; + private readonly ConcurrentDictionary _sessions = new(); + private readonly ConcurrentDictionary _transactions = new(); + private readonly ConcurrentDictionary _transactionOptions = new(); + private readonly ConcurrentDictionary _abortedTransactions = new(); + private bool _abortNextStatement; + private readonly ConcurrentDictionary _executionTimes = new(); + + public MockSpannerService() + { + AddDialectResult(); + } + + public void AddOrUpdateStatementResult(string sql, StatementResult result) + { + _results.AddOrUpdate(sql.Trim(), + result, + (_, _) => result + ); + } + + public void AddOrUpdateExecutionTime(string method, ExecutionTime executionTime) + { + _executionTimes.AddOrUpdate(method, + executionTime, + (_, _) => executionTime + ); + } + + private void AddDialectResult() + { + AddOrUpdateStatementResult(s_dialect_query, + StatementResult.CreateResultSet( + new List> + { + Tuple.Create(Spanner.V1.TypeCode.String, "option_value"), + }, + new List + { + new object[] { "GOOGLE_STANDARD_SQL" }, + })); + } + + internal void AbortTransaction(string transactionId) + { + _abortedTransactions.TryAdd(ByteString.FromBase64(transactionId), true); + } + + internal void AbortNextStatement() + { + lock (_lock) + { + _abortNextStatement = true; + } + } + + public IEnumerable Requests => new List(_requests).AsReadOnly(); + + public IEnumerable Contexts => new List(_contexts).AsReadOnly(); + + public IEnumerable Headers => new List(_headers).AsReadOnly(); + + public void Reset() + { + _requests = new ConcurrentQueue(); + _contexts = new ConcurrentQueue(); + _headers = new ConcurrentQueue(); + _executionTimes.Clear(); + _results.Clear(); + _abortedTransactions.Clear(); + _abortNextStatement = false; + AddDialectResult(); + } + + public override Task BeginTransaction(BeginTransactionRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + TryFindSession(request.SessionAsSessionName); + Spanner.V1.Transaction tx = new Spanner.V1.Transaction(); + var id = Interlocked.Increment(ref _transactionCounter); + tx.Id = ByteString.CopyFromUtf8($"{request.SessionAsSessionName}/transactions/{id}"); + _transactions.TryAdd(tx.Id, tx); + return Task.FromResult(tx); + } + + public override Task Commit(CommitRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + TryFindSession(request.SessionAsSessionName); + if (request.TransactionCase == CommitRequest.TransactionOneofCase.TransactionId) + { + TryFindTransaction(request.TransactionId, true); + } + CommitResponse response = new CommitResponse(); + Timestamp ts = Timestamp.FromDateTime(DateTime.UtcNow); + response.CommitTimestamp = ts; + return Task.FromResult(response); + } + + public override Task Rollback(RollbackRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + TryFindSession(request.SessionAsSessionName); + _transactions.TryRemove(request.TransactionId, out _); + return Task.FromResult(s_empty); + } + + private Session CreateSession(DatabaseName database, bool multiplexed) + { + var id = Interlocked.Increment(ref _sessionCounter); + Session session = new Session + { + SessionName = new SessionName(database.ProjectId, database.InstanceId, database.DatabaseId, $"session-{id}"), + Multiplexed = multiplexed, + }; + if (!_sessions.TryAdd(session.SessionName, session)) + { + throw new RpcException(new GrpcCore.Status(StatusCode.AlreadyExists, $"Session with id session-{id} already exists")); + } + return session; + } + + internal static RpcException CreateAbortedException(string message) + { + // Add a 100 nanosecond retry delay to the error to ensure that the delay is used, but does not slow + // down the tests unnecessary (100ns == 1 Tick is the smallest possible measurable timespan in .NET). + var key = RetryInfo.Descriptor.FullName + "-bin"; + var entry = new Metadata.Entry(key, new RetryInfo { RetryDelay = new Duration { Nanos = 100 } }.ToByteArray()); + var trailers = new Metadata { entry }; + + var status = new GrpcCore.Status(StatusCode.Aborted, $"Transaction aborted: {message}"); + var rpc = new RpcException(status, trailers); + + return rpc; + } + + internal static RpcException CreateDatabaseNotFoundException(string name) + { + var key = ResourceInfo.Descriptor.FullName + "-bin"; + var entry = new Metadata.Entry(key, new ResourceInfo { ResourceName = name, ResourceType = "type.googleapis.com/google.spanner.admin.database.v1.Database"}.ToByteArray()); + var trailers = new Metadata { entry }; + + var status = new GrpcCore.Status(StatusCode.NotFound, $"Database not found: Database with id {name} not found"); + var rpc = new RpcException(status, trailers); + + return rpc; + } + + private Spanner.V1.Transaction TryFindTransaction(ByteString id, Boolean remove = false) + { + if (_abortedTransactions.TryGetValue(id, out bool aborted) && aborted) + { + throw CreateAbortedException("Transaction marked as aborted"); + } + lock (_lock) + { + if (_abortNextStatement) + { + _abortNextStatement = false; + throw CreateAbortedException("Next statement was aborted"); + } + } + if (remove ? _transactions.TryRemove(id, out Spanner.V1.Transaction tx) : _transactions.TryGetValue(id, out tx)) + { + return tx; + } + throw new RpcException(new GrpcCore.Status(StatusCode.NotFound, $"Transaction not found: {id.ToBase64()}")); + } + + private Spanner.V1.Transaction FindOrBeginTransaction(SessionName session, TransactionSelector selector) + { + if (selector == null) + { + return BeginTransaction(session, s_singleUse, true); + } + // TODO: Check that the selected transaction actually belongs to the given session. + return selector.SelectorCase switch + { + TransactionSelector.SelectorOneofCase.SingleUse => BeginTransaction(session, selector.SingleUse, true), + TransactionSelector.SelectorOneofCase.Begin => BeginTransaction(session, selector.Begin, false), + TransactionSelector.SelectorOneofCase.Id => TryFindTransaction(selector.Id), + _ => null, + }; + } + + private Spanner.V1.Transaction BeginTransaction(SessionName session, TransactionOptions options, bool singleUse) + { + Spanner.V1.Transaction tx = new Spanner.V1.Transaction(); + var id = Interlocked.Increment(ref _transactionCounter); + tx.Id = ByteString.CopyFromUtf8($"{session}/transactions/{id}"); + if (options.ModeCase == TransactionOptions.ModeOneofCase.ReadOnly && options.ReadOnly.ReturnReadTimestamp) + { + tx.ReadTimestamp = Timestamp.FromDateTime(DateTime.UtcNow); + } + if (!singleUse) + { + _transactions.TryAdd(tx.Id, tx); + _transactionOptions.TryAdd(tx.Id, options); + } + return tx; + } + + public override Task BatchCreateSessions(BatchCreateSessionsRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + _executionTimes.TryGetValue(nameof(ExecuteStreamingSql), out ExecutionTime executionTime); + executionTime?.SimulateExecutionTime(); + var database = request.DatabaseAsDatabaseName; + BatchCreateSessionsResponse response = new BatchCreateSessionsResponse(); + for (int i = 0; i < request.SessionCount; i++) + { + response.Session.Add(CreateSession(database, false)); + } + return Task.FromResult(response); + } + + public override Task CreateSession(CreateSessionRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + var database = request.DatabaseAsDatabaseName; + return Task.FromResult(CreateSession(database, request.Session?.Multiplexed ?? false)); + } + + public override Task GetSession(GetSessionRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + return Task.FromResult(TryFindSession(request.SessionName)); + } + + public override Task ListSessions(ListSessionsRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + ListSessionsResponse response = new ListSessionsResponse(); + foreach (Session session in _sessions.Values) + { + response.Sessions.Add(session); + } + return Task.FromResult(response); + } + + public override Task DeleteSession(DeleteSessionRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + _sessions.TryRemove(request.SessionName, out _); + return Task.FromResult(s_empty); + } + + private Session TryFindSession(SessionName name) + { + if (_sessions.TryGetValue(name, out Session session)) + { + return session; + } + throw new RpcException(new GrpcCore.Status(StatusCode.NotFound, $"Session not found: {name}")); + } + + public override Task ExecuteBatchDml(ExecuteBatchDmlRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + _executionTimes.TryGetValue(nameof(ExecuteBatchDml), out ExecutionTime executionTime); + executionTime?.SimulateExecutionTime(); + _ = TryFindSession(request.SessionAsSessionName); + var tx = FindOrBeginTransaction(request.SessionAsSessionName, request.Transaction); + var response = new ExecuteBatchDmlResponse + { + // TODO: Return other statuses based on the mocked results. + Status = new Status + { + Code = (int)StatusCode.OK + } + }; + var index = 0; + foreach (var statement in request.Statements) + { + if (response.Status.Code != (int)StatusCode.OK) + { + break; + } + if (_results.TryGetValue(statement.Sql.Trim(), out StatementResult result)) + { + switch (result.Type) + { + case StatementResult.StatementResultType.ResultSet: + throw new RpcException(new GrpcCore.Status(StatusCode.InvalidArgument, $"ResultSet is not a valid result type for BatchDml")); + case StatementResult.StatementResultType.UpdateCount: + if (_executionTimes.TryGetValue(nameof(ExecuteBatchDml) + statement.Sql, out executionTime)) + { + executionTime.SimulateExecutionTime(); + } + var resultSet = CreateUpdateCountResultSet(result.UpdateCount); + if (index == 0 && request.Transaction?.Begin != null && tx != null) + { + resultSet.Metadata.Transaction = tx; + } + response.ResultSets.Add(resultSet); + break; + case StatementResult.StatementResultType.Exception: + if (index == 0) + { + throw result.Exception; + } + response.Status = StatusFromException(result.Exception); + break; + default: + throw new RpcException(new GrpcCore.Status(StatusCode.InvalidArgument, $"Invalid result type {result.Type} for {statement.Sql}")); + } + } + else + { + throw new RpcException(new GrpcCore.Status(StatusCode.InvalidArgument, $"No result found for {statement.Sql}")); + } + index++; + } + return Task.FromResult(response); + } + + private Status StatusFromException(Exception e) + { + if (e is RpcException rpc) + { + return new Status { Code = (int)rpc.StatusCode, Message = e.Message }; + } + return new Status { Code = (int)StatusCode.Unknown, Message = e.Message }; + } + + public override async Task ExecuteStreamingSql(ExecuteSqlRequest request, IServerStreamWriter responseStream, ServerCallContext context) + { + if (!request.Sql.Equals(s_dialect_query)) + { + _requests.Enqueue(request); + } + _contexts.Enqueue(context); + _headers.Enqueue(context.RequestHeaders); + _executionTimes.TryGetValue(nameof(ExecuteStreamingSql) + request.Sql, out ExecutionTime executionTime); + if (executionTime == null) + { + _executionTimes.TryGetValue(nameof(ExecuteStreamingSql), out executionTime); + } + executionTime?.SimulateExecutionTime(); + TryFindSession(request.SessionAsSessionName); + Spanner.V1.Transaction returnTransaction = null; + var transaction = FindOrBeginTransaction(request.SessionAsSessionName, request.Transaction); + if (request.Transaction != null && (request.Transaction.SelectorCase == TransactionSelector.SelectorOneofCase.Begin || request.Transaction.SelectorCase == TransactionSelector.SelectorOneofCase.SingleUse)) + { + returnTransaction = transaction; + } + if (_results.TryGetValue(request.Sql.Trim(), out StatementResult result)) + { + switch (result.Type) + { + case StatementResult.StatementResultType.ResultSet: + await WriteResultSet(returnTransaction, result.ResultSet, responseStream, executionTime); + break; + case StatementResult.StatementResultType.UpdateCount: + await WriteUpdateCount(returnTransaction, result.UpdateCount, responseStream); + break; + case StatementResult.StatementResultType.Exception: + throw result.Exception; + default: + throw new RpcException(new GrpcCore.Status(StatusCode.InvalidArgument, $"Invalid result type {result.Type} for {request.Sql}")); + } + } + else + { + throw new RpcException(new GrpcCore.Status(StatusCode.InvalidArgument, $"No result found for {request.Sql}")); + } + } + + private async Task WriteResultSet(Spanner.V1.Transaction transaction, ResultSet resultSet, IServerStreamWriter responseStream, ExecutionTime executionTime) + { + int index = 0; + PartialResultSetsEnumerable enumerator = new PartialResultSetsEnumerable(transaction, resultSet); + int writePermissions = executionTime?.TakeWritePermission() ?? int.MaxValue; + foreach (PartialResultSet prs in enumerator) + { + Exception e = executionTime?.PopExceptionAtIndex(index); + if (e != null) + { + throw e; + } + await responseStream.WriteAsync(prs); + index++; + writePermissions--; + if (writePermissions == 0) + { + writePermissions = executionTime?.TakeWritePermission() ?? int.MaxValue; + } + } + } + + private async Task WriteUpdateCount(Spanner.V1.Transaction transaction, long updateCount, IServerStreamWriter responseStream) + { + PartialResultSet prs = new PartialResultSet + { + Metadata = new ResultSetMetadata { Transaction = transaction, RowType = new StructType()}, + Stats = new ResultSetStats { RowCountExact = updateCount } + }; + await responseStream.WriteAsync(prs); + } + + private ResultSet CreateUpdateCountResultSet(long updateCount) + { + ResultSet rs = new ResultSet + { + Metadata = new ResultSetMetadata { RowType = new StructType()}, + Stats = new ResultSetStats { RowCountExact = updateCount } + }; + return rs; + } + } + + public class MockDatabaseAdminService : DatabaseAdmin.DatabaseAdminBase + { + private readonly ConcurrentQueue _requests = new ConcurrentQueue(); + + public IEnumerable Requests => new List(_requests).AsReadOnly(); + + public override Task CreateDatabase(CreateDatabaseRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + var op = new Operation + { + Name = "projects/p1/instances/i1/operations/o1", + Response = Any.Pack(new Database()), + Metadata = Any.Pack(new CreateDatabaseMetadata()), + Done = true, + }; + return Task.FromResult(op); + } + + public override Task UpdateDatabaseDdl(UpdateDatabaseDdlRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + var op = new Operation + { + Name = "projects/p1/instances/i1/operations/o1", + Response = Any.Pack(new Empty()), + Metadata = Any.Pack(new UpdateDatabaseDdlMetadata()), + Done = true, + }; + return Task.FromResult(op); + } + + public override Task DropDatabase(DropDatabaseRequest request, ServerCallContext context) + { + _requests.Enqueue(request); + return Task.FromResult(new Empty()); + } + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/SpannerConverter.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/SpannerConverter.cs new file mode 100644 index 00000000..18b3e2bb --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/SpannerConverter.cs @@ -0,0 +1,146 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Linq; +using System.Xml; +using Google.Cloud.Spanner.V1; +using Google.Protobuf.WellKnownTypes; +using static System.Globalization.CultureInfo; +using TypeCode = Google.Cloud.Spanner.V1.TypeCode; + +namespace Google.Cloud.SpannerLib.Tests.MockServer +{ + internal static class SpannerConverter + { + internal static Value ToProtobufValue(Spanner.V1.Type type, object? value) + { + if (value == null || value is DBNull) + { + return Value.ForNull(); + } + + switch (type.Code) + { + case TypeCode.Bytes: + if (value is string s) + { + return new Value { StringValue = s }; + } + if (value is byte[] bArray) + { + return new Value { StringValue = Convert.ToBase64String(bArray) }; + } + throw new ArgumentException("TypeCode.Bytes only supports string and byte[]", nameof(value)); + case TypeCode.Bool: + return new Value { BoolValue = Convert.ToBoolean(value) }; + case TypeCode.String: + if (value is DateTime dateTime) + { + // If the value is a DateTime, we always convert using XmlConvert. + // This allows us to convert back to a datetime reliably from the + // resulting string (so roundtrip works properly if the developer uses + // a string as a backing field for a datetime for whatever reason). + return new Value { StringValue = XmlConvert.ToString(dateTime, XmlDateTimeSerializationMode.Utc) }; + } + return new Value { StringValue = Convert.ToString(value, InvariantCulture) }; + case TypeCode.Int64: + return new Value + { + StringValue = Convert.ToInt64(value, InvariantCulture) + .ToString(InvariantCulture) + }; + case TypeCode.Float32: + return new Value { NumberValue = Convert.ToSingle(value, InvariantCulture) }; + case TypeCode.Float64: + return new Value { NumberValue = Convert.ToDouble(value, InvariantCulture) }; + case TypeCode.Timestamp: + return new Value + { + StringValue = XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc) + }; + case TypeCode.Date: + return new Value + { + StringValue = StripTimePart( + XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc)) + }; + case TypeCode.Json: + if (value is string stringValue) + { + return new Value { StringValue = stringValue }; + } + throw new ArgumentException("JSON values must be given as string"); + case TypeCode.Array: + if (value is IEnumerable enumerable) + { + return Value.ForList( + enumerable.Cast() + .Select(x => ToProtobufValue(type.ArrayElementType, x)).ToArray()); + } + throw new ArgumentException("The given array instance needs to implement IEnumerable."); + + case TypeCode.Struct: + throw new ArgumentException("Struct values are not supported"); + + case TypeCode.Numeric: + if (value is SpannerNumeric spannerNumeric) + { + return Value.ForString(spannerNumeric.ToString()); + } + if (value is string str) + { + return Value.ForString(SpannerNumeric.Parse(str).ToString()); + } + if (value is float || value is double || value is decimal) + { + // We throw if there's a loss of precision. We could use + // LossOfPrecisionHandling.Truncate but GoogleSQL documentation requests to + // use half-away-from-zero rounding but the SpannerNumeric implementation + // truncates instead. + return Value.ForString(SpannerNumeric.FromDecimal( + Convert.ToDecimal(value, InvariantCulture), LossOfPrecisionHandling.Throw).ToString()); + } + if (value is sbyte || value is short || value is int || value is long) + { + SpannerNumeric numericValue = Convert.ToInt64(value, InvariantCulture); + return Value.ForString(numericValue.ToString()); + } + if (value is byte || value is ushort || value is uint || value is ulong) + { + SpannerNumeric numericValue = Convert.ToUInt64(value, InvariantCulture); + return Value.ForString(numericValue.ToString()); + } + throw new ArgumentException("Numeric parameters must be of type SpannerNumeric or string"); + + default: + throw new ArgumentOutOfRangeException(nameof(type.Code), type.Code, null); + } + } + + private static string StripTimePart(string rfc3339String) + { + if (!string.IsNullOrEmpty(rfc3339String)) + { + int timeIndex = rfc3339String.IndexOf('T'); + if (timeIndex != -1) + { + return rfc3339String.Substring(0, timeIndex); + } + } + return rfc3339String; + } + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/SpannerMockServerFixture.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/SpannerMockServerFixture.cs new file mode 100644 index 00000000..a7afdec8 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.MockServer/SpannerMockServerFixture.cs @@ -0,0 +1,83 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Configuration; + +namespace Google.Cloud.SpannerLib.MockServer; + +public class SpannerMockServerFixture : IDisposable +{ + private readonly Random _random = new (); + + private readonly IWebHost _host; + + public MockSpannerService SpannerMock { get; } + public MockDatabaseAdminService DatabaseAdminMock { get; } + public string Endpoint => $"localhost:{Port}"; + public string Host => "localhost"; + public int Port { get; } + + public SpannerMockServerFixture() + { + SpannerMock = new MockSpannerService(); + SpannerMock.AddOrUpdateStatementResult( + "select option_value from information_schema.database_options where option_name='database_dialect'", + StatementResult.CreateResultSet( + new List> + { + Tuple.Create(Spanner.V1.TypeCode.String, "option_value"), + }, + new List + { + new object[] { "GOOGLE_STANDARD_SQL" }, + })); + DatabaseAdminMock = new MockDatabaseAdminService(); + + var endpoint = IPEndPoint.Parse("127.0.0.1:0"); + var builder = WebHost.CreateDefaultBuilder(); + builder.ConfigureAppConfiguration(configurationBuilder => configurationBuilder.AddJsonFile("appsettings.json")); + builder.UseStartup(_ => new MockServerStartup(SpannerMock, DatabaseAdminMock)); + builder.ConfigureKestrel(options => + { + // Setup a HTTP/2 endpoint without TLS. + options.Listen(endpoint, o => o.Protocols = HttpProtocols.Http2); + }); + _host = builder.Build(); + _host.Start(); + var address = _host.ServerFeatures.Get()!.Addresses.First(); + var uri = new Uri(address); + Port = uri.Port; + } + + public void Dispose() + { + _host.StopAsync().Wait(); + } + + public long RandomLong(long min = 0, long max = long.MaxValue) + { + var buf = new byte[8]; + _random.NextBytes(buf); + var longRand = BitConverter.ToInt64(buf, 0); + return (Math.Abs(longRand % (max - min)) + min); + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/.gitignore b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/.gitignore new file mode 100644 index 00000000..1aa3ee1c --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/.gitignore @@ -0,0 +1 @@ +libraries diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/GoSlice.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/GoSlice.cs new file mode 100644 index 00000000..fb7c10b0 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/GoSlice.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.Cloud.SpannerLib.Native +{ + + internal class DisposableGoSlice : IDisposable + { + private readonly GCHandle? _handle; + internal GoSlice GoSlice { get; } + + internal static DisposableGoSlice Create(byte[]? value) + { + var (handle, length, capacity) = Pin(value); + return new DisposableGoSlice(handle, + new GoSlice(handle?.AddrOfPinnedObject() ?? IntPtr.Zero, length, capacity)); + } + + private DisposableGoSlice(GCHandle? handle, GoSlice goSlice) + { + _handle = handle; + GoSlice = goSlice; + } + + private static (GCHandle?, int, int) Pin(byte[]? value) + { + if (value is null) + { + return (null, 0, 0); + } + + var length = value.Length; + var handle = GCHandle.Alloc(value, GCHandleType.Pinned); + return (handle, length, length); + } + + public void Dispose() + { + _handle?.Free(); + } + + } + + internal struct GoSlice + { + public IntPtr Pointer; + public long Length; + public long Capacity; + + internal GoSlice(IntPtr pointer, long length, long capacity) + { + Pointer = pointer; + Length = length; + Capacity = capacity; + } + + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/GoString.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/GoString.cs new file mode 100644 index 00000000..1e318369 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/GoString.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Google.Cloud.SpannerLib.Native +{ + + internal unsafe struct GoString : IDisposable + { + IntPtr Pointer { get; } + internal long Length; + + internal GoString(string value) + { + (Pointer, Length) = StringToHGlobalUtf8(value); + } + + private static (IntPtr, int) StringToHGlobalUtf8(string? s) + { + if (s is null) + { + return (IntPtr.Zero, 0); + } + + var nb = Encoding.UTF8.GetMaxByteCount(s.Length); + var ptr = Marshal.AllocHGlobal(nb); + + var pbMem = (byte*)ptr; + var nbWritten = Encoding.UTF8.GetBytes(s, new Span(pbMem, nb)); + + return (ptr, nbWritten); + } + + public void Dispose() + { + Marshal.FreeHGlobal(Pointer); + } + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/Google.Cloud.SpannerLib.Native.csproj b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/Google.Cloud.SpannerLib.Native.csproj new file mode 100644 index 00000000..3df21d6b --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/Google.Cloud.SpannerLib.Native.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.1 + enable + true + 9 + Experimental.SpannerLib.Native + Experimental native library for Spanner + + 1.0.11 + + + + + + + + + diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/Message.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/Message.cs new file mode 100644 index 00000000..da3bde81 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/Message.cs @@ -0,0 +1,13 @@ +namespace Google.Cloud.SpannerLib.Native +{ + internal unsafe struct Message + { +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + public long Pinner; + public int Code; + public long ObjectId; + public int Length; + public void* Pointer; +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/MessageHandler.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/MessageHandler.cs new file mode 100644 index 00000000..c8088e58 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/MessageHandler.cs @@ -0,0 +1,57 @@ +using System; +using System.Data; +using System.Text; + +namespace Google.Cloud.SpannerLib.Native +{ + + internal class MessageHandler : IDisposable + { + private Message Message { get; } + + internal long Length => Message.Length; + + internal MessageHandler(Message message) + { + Message = message; + } + + internal int Code() + { + return Message.Code; + } + + internal long ObjectId() + { + return Message.ObjectId; + } + + internal bool HasError() + { + return Message.Code != 0; + } + + internal string ValueAsString() + { + unsafe + { + Span tmp = new(Message.Pointer, Message.Length); + return Encoding.UTF8.GetString(tmp); + } + } + + internal unsafe ReadOnlySpan Value() + { + return new(Message.Pointer, Message.Length); + } + + public void Dispose() + { + var code = SpannerLib.Release(Message.Pinner); + if (code != 0) + { + throw new DataException("Failed to release message"); + } + } + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/SpannerLib.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/SpannerLib.cs new file mode 100644 index 00000000..90591823 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/SpannerLib.cs @@ -0,0 +1,72 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: InternalsVisibleTo("Google.Cloud.SpannerLib")] + +namespace Google.Cloud.SpannerLib.Native +{ + public static class SpannerLib + { + private const string SpannerLibName = "spannerlib"; + + [DllImport(SpannerLibName, EntryPoint = "Release")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern int Release(long pinner); + + [DllImport(SpannerLibName, EntryPoint = "CreatePool")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message CreatePool(GoString dsn); + + [DllImport(SpannerLibName, EntryPoint = "ClosePool")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message ClosePool(long poolId); + + [DllImport(SpannerLibName, EntryPoint = "CreateConnection")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message CreateConnection(long poolId); + + [DllImport(SpannerLibName, EntryPoint = "CloseConnection")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message CloseConnection(long poolId, long connectionId); + + [DllImport(SpannerLibName, EntryPoint = "WriteMutations")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message WriteMutations(long poolId, long connectionId, GoSlice mutations); + + [DllImport(SpannerLibName, EntryPoint = "Execute")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message Execute(long poolId, long connectionId, GoSlice statement); + + [DllImport(SpannerLibName, EntryPoint = "ExecuteBatch")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message ExecuteBatch(long poolId, long connectionId, GoSlice statements); + + [DllImport(SpannerLibName, EntryPoint = "Metadata")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message Metadata(long poolId, long connectionId, long rowsId); + + [DllImport(SpannerLibName, EntryPoint = "ResultSetStats")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message ResultSetStats(long poolId, long connectionId, long rowsId); + + [DllImport(SpannerLibName, EntryPoint = "Next")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message Next(long poolId, long connectionId, long rowsId, int numRows, int encodeRowOption); + + [DllImport(SpannerLibName, EntryPoint = "CloseRows")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message CloseRows(long poolId, long connectionId, long rowsId); + + [DllImport(SpannerLibName, EntryPoint = "BeginTransaction")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message BeginTransaction(long poolId, long connectionId, GoSlice transactionOptions); + + [DllImport(SpannerLibName, EntryPoint = "Commit")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message Commit(long poolId, long connectionId); + + [DllImport(SpannerLibName, EntryPoint = "Rollback")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + internal static extern Message Rollback(long poolId, long connectionId); + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/build-cpp.sh b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/build-cpp.sh new file mode 100644 index 00000000..d59c6631 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/build-cpp.sh @@ -0,0 +1,6 @@ + +# Clear all local nuget cache +dotnet nuget locals --clear all +dotnet pack +dotnet nuget remove source local 2>/dev/null +dotnet nuget add source "$PWD"/bin/Release --name local diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/build.sh b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/build.sh new file mode 100755 index 00000000..8bd713f0 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Native/build.sh @@ -0,0 +1,12 @@ + +# Determine OS + Arch +export DEST=libraries/any/spannerlib +mkdir -p libraries/any + +# Clear all local nuget cache +dotnet nuget locals --clear all +go build -o ../../shared/spannerlib.so -buildmode=c-shared ../../shared/shared_lib.go +cp ../../shared/spannerlib.so $DEST +dotnet pack +dotnet nuget remove source local 2>/dev/null +dotnet nuget add source "$PWD"/bin/Release --name local diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/BasicTests.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/BasicTests.cs new file mode 100644 index 00000000..ceced7a6 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/BasicTests.cs @@ -0,0 +1,360 @@ +using System.Diagnostics; +using Google.Cloud.Spanner.V1; +using Google.Cloud.SpannerLib.MockServer; +using Google.Cloud.SpannerLib.Tests.MockServer; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; +using TypeCode = Google.Cloud.Spanner.V1.TypeCode; + +namespace Google.Cloud.SpannerLib.Tests; + +public class BasicTests +{ + private SpannerMockServerFixture _fixture; + + private string ConnectionString => $"{_fixture.Host}:{_fixture.Port}/projects/p1/instances/i1/databases/d1;UsePlainText=true"; + + [SetUp] + public void Setup() + { + _fixture = new SpannerMockServerFixture(); + _fixture.SpannerMock.AddOrUpdateStatementResult("SELECT 1", StatementResult.CreateSelect1ResultSet()); + } + + [TearDown] + public void Teardown() + { + _fixture.Dispose(); + } + + [Test] + public void TestCreatePool() + { + var pool = Pool.Create(ConnectionString); + pool.Close(); + } + + [Test] + public void TestCreateConnection() + { + using var pool = Pool.Create(ConnectionString); + var connection = pool.CreateConnection(); + connection.Close(); + } + + [Test] + public void TestExecuteQuery() + { + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + using var rows = connection.Execute(new ExecuteSqlRequest { Sql = "SELECT 1" }); + for (var row = rows.Next(); row != null; row = rows.Next()) + { + Assert.That(row.Values.Count, Is.EqualTo(1)); + Assert.That(row.Values[0].HasStringValue); + Assert.That(row.Values[0].StringValue, Is.EqualTo("1")); + } + } + + [Test] + public void TestExecuteQueryError() + { + var sql = "select * from non_existing_table"; + _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateException(new RpcException(new Status(StatusCode.NotFound, "Table not found")))); + + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + SpannerException exception = Assert.Throws(() => connection.Execute(new ExecuteSqlRequest { Sql = sql })); + Assert.That(exception.ErrorCode, Is.EqualTo(ErrorCode.NotFound)); + Assert.That(exception.Message, Is.EqualTo("Table not found")); + } + + [Test] + public void TestExecuteParameterizedQuery() + { + var sql = "select col_varchar from all_types where col_bigint=$1::bigint"; + _fixture.SpannerMock.AddOrUpdateStatementResult(sql, + StatementResult.CreateSingleColumnResultSet( + new Spanner.V1.Type { Code = TypeCode.String }, + "col_varchar", "some-value")); + + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + var parameters = new Struct + { + Fields = + { + ["p1"] = Value.ForString("1") + } + }; + using var rows = connection.Execute(new ExecuteSqlRequest + { + Sql = sql, + Params = parameters, + }); + for (var row = rows.Next(); row != null; row = rows.Next()) + { + Assert.That(row.Values.Count, Is.EqualTo(1)); + Assert.That(row.Values[0].HasStringValue); + Assert.That(row.Values[0].StringValue, Is.EqualTo("some-value")); + } + } + + [Test] + public void TestQueryParameterStartingWithUnderscore() + { + var sql = "select col_string from all_types where col_int64=@__id"; + _fixture.SpannerMock.AddOrUpdateStatementResult(sql, + StatementResult.CreateSingleColumnResultSet( + new Spanner.V1.Type { Code = TypeCode.String }, + "col_string", "some-value")); + + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + var parameters = new Struct + { + Fields = + { + ["__id"] = Value.ForString("1") + } + }; + using var rows = connection.Execute(new ExecuteSqlRequest + { + Sql = sql, + Params = parameters, + }); + for (var row = rows.Next(); row != null; row = rows.Next()) + { + Assert.That(row.Values.Count, Is.EqualTo(1)); + Assert.That(row.Values[0].HasStringValue); + Assert.That(row.Values[0].StringValue, Is.EqualTo("some-value")); + } + } + + [Test] + [Ignore("execute async disabled for now")] + public async Task TestExecuteQueryAsync() + { + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + using var rows = await connection.ExecuteAsync(new ExecuteSqlRequest { Sql = "SELECT 1" }); + var metadata = rows.Metadata; + Assert.That(metadata!.RowType.Fields.Count, Is.EqualTo(1)); + for (var row = await rows.NextAsync(); row != null; row = await rows.NextAsync()) + { + Assert.That(row.Values.Count, Is.EqualTo(1)); + Assert.That(row.Values[0].HasStringValue); + Assert.That(row.Values[0].StringValue, Is.EqualTo("1")); + } + } + + [Test] + public void TestReadOnlyTransaction() + { + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + using var transaction = connection.BeginTransaction(new TransactionOptions + { ReadOnly = new TransactionOptions.Types.ReadOnly() }); + using var rows = transaction.Execute(new ExecuteSqlRequest { Sql = "SELECT 1" }); + for (var row = rows.Next(); row != null; row = rows.Next()) + { + Assert.That(row.Values.Count, Is.EqualTo(1)); + Assert.That(row.Values[0].HasStringValue); + Assert.That(row.Values[0].StringValue, Is.EqualTo("1")); + } + var commitResponse = transaction.Commit(); + Assert.That(commitResponse, Is.Not.Null); + } + + [Test] + public void TestReadWriteTransaction() + { + var sql = "update table1 set value='one' where id=1"; + _fixture.SpannerMock.AddOrUpdateStatementResult(sql, StatementResult.CreateUpdateCount(1)); + using var pool = Pool.Create(ConnectionString); + using var connection = pool.CreateConnection(); + using var transaction = connection.BeginTransaction(new TransactionOptions + { ReadWrite = new TransactionOptions.Types.ReadWrite() }); + using var rows = transaction.Execute(new ExecuteSqlRequest { Sql = sql }); + Assert.That(rows.UpdateCount, Is.EqualTo(1)); + var commitResponse = transaction.Commit(); + Assert.That(commitResponse, Is.Not.Null); + } + + [Test] + [Ignore("for local testing")] + public void TestBenchmarkNativeSpannerLib() + { + var totalRowCount = 1000000; + _fixture.SpannerMock.AddOrUpdateStatementResult( + "select * from all_types", + StatementResult.CreateResultSet( + new List> + { + Tuple.Create(TypeCode.String, "col1"), + Tuple.Create(TypeCode.String, "col2"), + Tuple.Create(TypeCode.String, "col3"), + Tuple.Create(TypeCode.String, "col4"), + Tuple.Create(TypeCode.String, "col5"), + }, + GenerateRandomValues(totalRowCount))); + + var spanner = new SharedLibSpanner(); + using var pool = Pool.Create(spanner, ConnectionString); + using var connection = pool.CreateConnection(); + + var stopwatch = Stopwatch.StartNew(); + using var rows = connection.Execute(new ExecuteSqlRequest { Sql = "select * from all_types" }); + var rowCount = 0; + for (var row = rows.Next(); row != null; row = rows.Next()) + { + rowCount++; + } + Assert.That(rowCount, Is.EqualTo(totalRowCount)); + stopwatch.Stop(); + Console.WriteLine(stopwatch.Elapsed); + } + + [Test] + [Ignore("for local testing")] + public void TestBenchmarkDotnetGrpcClient() + { + var totalRowCount = 1000000; + _fixture.SpannerMock.AddOrUpdateStatementResult( + "select * from all_types", + StatementResult.CreateResultSet( + new List> + { + Tuple.Create(TypeCode.String, "col1"), + Tuple.Create(TypeCode.String, "col2"), + Tuple.Create(TypeCode.String, "col3"), + Tuple.Create(TypeCode.String, "col4"), + Tuple.Create(TypeCode.String, "col5"), + }, + GenerateRandomValues(totalRowCount))); + var totalValueCount = totalRowCount * 5; + var builder = new SpannerClientBuilder + { + Endpoint = $"http://{_fixture.Endpoint}", + ChannelCredentials = ChannelCredentials.Insecure + }; + SpannerClient client = builder.Build(); + var request = new CreateSessionRequest + { + Database = "projects/p1/instances/i1/databases/d1", + Session = new Session() + }; + var session = client.CreateSession(request); + Assert.That(session, Is.Not.Null); + + var stopwatch = Stopwatch.StartNew(); + var executeRequest = new ExecuteSqlRequest + { + Sql = "select * from all_types", + Session = session.Name, + }; + var stream = client.ExecuteStreamingSql(executeRequest); + var valueCount = 0; + foreach (var result in stream.GetResponseStream().ToBlockingEnumerable()) + { + Assert.That(result, Is.Not.Null); + valueCount += result.Values.Count; + if (result.ChunkedValue) + { + valueCount--; + } + } + Assert.That(valueCount, Is.EqualTo(totalValueCount)); + stopwatch.Stop(); + Console.WriteLine(stopwatch.Elapsed); + } + + [Test] + [Ignore("for local testing")] + public async Task TestBenchmarkGrpcSpannerLib() + { + var totalRowCount = 1000000; + _fixture.SpannerMock.AddOrUpdateStatementResult( + "select * from all_types", + StatementResult.CreateResultSet( + new List> + { + Tuple.Create(TypeCode.String, "col1"), + Tuple.Create(TypeCode.String, "col2"), + Tuple.Create(TypeCode.String, "col3"), + Tuple.Create(TypeCode.String, "col4"), + Tuple.Create(TypeCode.String, "col5"), + }, + GenerateRandomValues(totalRowCount))); + + var spanner = new GrpcLibSpanner(); + using var pool = Pool.Create(spanner, ConnectionString); + using var connection = pool.CreateConnection(); + + var stopwatch = Stopwatch.StartNew(); + using var rows = connection.Execute(new ExecuteSqlRequest { Sql = "select * from all_types" }); + var rowCount = 0; + for (var row = await rows.NextAsync(); row != null; row = await rows.NextAsync()) + { + rowCount++; + } + Assert.That(rowCount, Is.EqualTo(totalRowCount)); + stopwatch.Stop(); + Console.WriteLine(stopwatch.Elapsed); + } + + [Test] + [Ignore("for local testing")] + public async Task TestBenchmarkGrpcSpannerLibAsync() + { + var totalRowCount = 1000000; + _fixture.SpannerMock.AddOrUpdateStatementResult( + "select * from all_types", + StatementResult.CreateResultSet( + new List> + { + Tuple.Create(TypeCode.String, "col1"), + Tuple.Create(TypeCode.String, "col2"), + Tuple.Create(TypeCode.String, "col3"), + Tuple.Create(TypeCode.String, "col4"), + Tuple.Create(TypeCode.String, "col5"), + }, + GenerateRandomValues(totalRowCount))); + + var spanner = new GrpcLibSpanner(); + using var pool = Pool.Create(spanner, ConnectionString); + using var connection = pool.CreateConnection(); + + var stopwatch = Stopwatch.StartNew(); + using var rows = await connection.ExecuteAsync(new ExecuteSqlRequest { Sql = "select * from all_types" }); + var rowCount = 0; + for (var row = await rows.NextAsync(); row != null; row = await rows.NextAsync()) + { + rowCount++; + } + Assert.That(rowCount, Is.EqualTo(totalRowCount)); + stopwatch.Stop(); + Console.WriteLine(stopwatch.Elapsed); + } + + private List GenerateRandomValues(int count) + { + var result = new List(count); + for (var i = 0; i < count; i++) + { + result.Add([ + GenerateRandomString(), + GenerateRandomString(), + GenerateRandomString(), + GenerateRandomString(), + GenerateRandomString(), + ]); + } + return result; + } + + private string GenerateRandomString() + { + return Guid.NewGuid().ToString(); + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/Google.Cloud.SpannerLib.Tests.csproj b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/Google.Cloud.SpannerLib.Tests.csproj new file mode 100644 index 00000000..4a77d4a9 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/Google.Cloud.SpannerLib.Tests.csproj @@ -0,0 +1,33 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/appsettings.json b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/appsettings.json new file mode 100644 index 00000000..2a8537d3 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib.Tests/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Microsoft": "Warning" + } + } +} diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/AbstractLibObject.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/AbstractLibObject.cs new file mode 100644 index 00000000..1f16f66b --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/AbstractLibObject.cs @@ -0,0 +1,69 @@ +using System; + +namespace Google.Cloud.SpannerLib +{ + public abstract class AbstractLibObject : IDisposable + { + internal ISpanner Spanner { get; } + internal long Id { get; } + + private bool _disposed; + + protected void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + internal AbstractLibObject(ISpanner spanner, long id) + { + Spanner = spanner; + Id = id; + } + + ~AbstractLibObject() + { + Dispose(false); + } + + protected void MarkDisposed() + { + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Close() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + try + { + if (Id > 0) + { + CloseLibObject(); + } + } + finally + { + _disposed = true; + } + } + + protected abstract void CloseLibObject(); + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Connection.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Connection.cs new file mode 100644 index 00000000..83b9053f --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Connection.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Google.Cloud.Spanner.V1; + +namespace Google.Cloud.SpannerLib; + +public class Connection : AbstractLibObject +{ + internal Pool Pool { get; } + + internal Connection(Pool pool, long id) : base(pool.Spanner, id) + { + Pool = pool; + } + + public Transaction BeginTransaction(TransactionOptions transactionOptions) + { + return Spanner.BeginTransaction(this, transactionOptions); + } + + public CommitResponse WriteMutations(BatchWriteRequest.Types.MutationGroup mutations) + { + return Spanner.WriteMutations(this, mutations); + } + + public Rows Execute(ExecuteSqlRequest statement) + { + return Spanner.Execute(this, statement); + } + + public Task ExecuteAsync(ExecuteSqlRequest statement) + { + return Spanner.ExecuteAsync(this, statement); + } + + public long[] ExecuteBatch(List statements) + { + var request = new ExecuteBatchDmlRequest + { + Statements = { statements } + }; + return Spanner.ExecuteBatch(this, request); + } + + public Task ExecuteBatchAsync(List statements) + { + var request = new ExecuteBatchDmlRequest + { + Statements = { statements } + }; + return Spanner.ExecuteBatchAsync(this, request); + } + + protected override void CloseLibObject() + { + Spanner.CloseConnection(this); + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/ErrorCode.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/ErrorCode.cs new file mode 100644 index 00000000..6fdbbfe3 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/ErrorCode.cs @@ -0,0 +1,89 @@ +namespace Google.Cloud.SpannerLib +{ + + public enum ErrorCode + { + /// The operation was cancelled. + Cancelled = 1, + + /// + /// Unknown error. This may happen if an internal error occured or + /// not enough information was obtained to produce a useful message. + /// + Unknown = 2, + + /// + /// A bad SQL statement or other invalid input was sent to Spanner. + /// + InvalidArgument = 3, + + /// + /// A timeout has occurred. A should be + /// retried if this error is encountered. + /// + DeadlineExceeded = 4, + + /// + /// The given resource (Spanner Instance, Database, Table) was not + /// found. + /// + NotFound = 5, + + /// + /// The given resource could be not created because it already exists. + /// + AlreadyExists = 6, + + /// + /// The supplied credential from + /// or from default application credentials does not have sufficient permission for the request. + /// + PermissionDenied = 7, + + /// + /// A resource associated with Spanner has been exhausted. This may occur due to a server-side + /// failure, or due to the local maximum number of sessions being reached. + /// + ResourceExhausted = 8, + + /// + /// Operation was rejected because the system is not in a state required for the + /// operation's execution. + /// + FailedPrecondition = 9, + + /// + /// The operation was aborted due to transient issue such as competing transaction + /// resources. A should be retried if this error + /// is encountered. + /// + Aborted = 10, // 0x0000000A + + /// The operation attempted to read past the valid range. + OutOfRange = 11, // 0x0000000B + + /// + /// Operation is not implemented or not supported/enabled in this service. + /// + Unimplemented = 12, // 0x0000000C + + /// Internal error. + Internal = 13, // 0x0000000D + + /// + /// The service is currently unavailable. This is a most likely a transient condition + /// and may be corrected by retrying. A should be + /// retried if this error is encountered. + /// + Unavailable = 14, // 0x0000000E + + /// Unrecoverable data loss or corruption. + DataLoss = 15, // 0x0000000F + + /// + /// There is no supplied credential either through default application credentials or + /// directly through + /// + Unauthenticated = 16, // 0x00000010 + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Google.Cloud.SpannerLib.csproj b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Google.Cloud.SpannerLib.csproj new file mode 100644 index 00000000..e3521d8b --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Google.Cloud.SpannerLib.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + true + default + + Experimental .NET library for Spanner + Experimental.SpannerLib + 1.0.11 + + + + + + + + + diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/GrpcLibSpanner.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/GrpcLibSpanner.cs new file mode 100644 index 00000000..d2810da6 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/GrpcLibSpanner.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Google.Cloud.Spanner.V1; +using Google.Cloud.SpannerLib.V1; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; +using ExecuteBatchDmlRequest = Google.Cloud.Spanner.V1.ExecuteBatchDmlRequest; + +namespace Google.Cloud.SpannerLib; + +public class GrpcLibSpanner : ISpanner +{ + private readonly Grpc.SpannerLib _lib = new(); + + private readonly Dictionary> _streams = new(); + + public Pool CreatePool(string dsn) + { + var pool = _lib.CreatePool(dsn); + return new Pool(this, pool.Id); + } + + public void ClosePool(Pool pool) + { + _lib.ClosePool(ToProto(pool)); + } + + public Connection CreateConnection(Pool pool) + { + var connection = _lib.CreateConnection(ToProto(pool)); + // var stream = CreateConnectionStream(); + // _streams.Add(connection.Id, stream); + return new Connection(pool, connection.Id); + } + + public void CloseConnection(Connection connection) + { + if (_streams.TryGetValue(connection.Id, out var stream)) + { + stream.RequestStream.CompleteAsync(); + _streams.Remove(connection.Id); + } + _lib.CloseConnection(ToProto(connection)); + } + + private AsyncDuplexStreamingCall CreateConnectionStream() + { + return _lib.CreateStream(); + } + + public CommitResponse WriteMutations(Connection connection, BatchWriteRequest.Types.MutationGroup mutations) + { + return _lib.Apply(ToProto(connection), mutations); + } + + public void BufferWrite(Transaction transaction, BatchWriteRequest.Types.MutationGroup mutations) + { + _lib.BufferWrite(ToProto(transaction), mutations); + } + + public Rows Execute(Connection connection, ExecuteSqlRequest statement) + { + var stream = _lib.ExecuteStreaming(ToProto(connection), statement); + return new Rows(connection, stream); + + // var rows = _lib.Execute(ToProto(connection), statement); + // return new Rows(connection, rows.Id); + } + + public async Task ExecuteAsync(Connection connection, ExecuteSqlRequest statement) + { + if (_streams.TryGetValue(connection.Id, out var stream)) + { + return await ExecuteStreaming(stream, connection, statement); + } + return await Task.Run(() => Execute(connection, statement)); + } + + private async Task ExecuteStreaming(AsyncDuplexStreamingCall stream, Connection connection, ExecuteSqlRequest statement) + { + await stream.RequestStream.WriteAsync(new ConnectionStreamRequest + { + ExecuteRequest = new ExecuteRequest + { + Connection = ToProto(connection), + ExecuteSqlRequest = statement, + } + }); + await stream.ResponseStream.MoveNext(); + var response = stream.ResponseStream.Current; + var rows = new Rows(connection, response.Rows.Id, false); + await rows.InitMetadataAsync(); + return rows; + } + + public Rows ExecuteTransaction(Transaction transaction, ExecuteSqlRequest statement) + { + var rows = _lib.ExecuteTransaction(ToProto(transaction), statement); + return new Rows(transaction.SpannerConnection, rows.Id); + } + + public long[] ExecuteBatch(Connection connection, ExecuteBatchDmlRequest statements) + { + var response = _lib.ExecuteBatchDml(ToProto(connection), statements); + var result = new long[response.ResultSets.Count]; + for (var i = 0; i < result.Length; i++) + { + result[i] = response.ResultSets[i].Stats.RowCountExact; + } + return result; + } + + public Task ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequest statements) + { + throw new System.NotImplementedException(); + } + + public ResultSetMetadata? Metadata(Rows rows) + { + return _lib.Metadata(ToProto(rows)); + } + + public async Task MetadataAsync(Rows rows) + { + if (_streams.TryGetValue(rows.SpannerConnection.Id, out var stream)) + { + return await MetadataStreaming(stream, rows); + } + return await Task.Run(() => Metadata(rows)); + } + + private async Task MetadataStreaming(AsyncDuplexStreamingCall stream, Rows rows) + { + await stream.RequestStream.WriteAsync(new ConnectionStreamRequest + { + MetadataRequest = new MetadataRequest + { + Rows = ToProto(rows), + } + }); + await stream.ResponseStream.MoveNext(); + var response = stream.ResponseStream.Current; + return response.Metadata; + } + + public ResultSetStats? Stats(Rows rows) + { + return _lib.ResultSetStats(ToProto(rows)); + } + + public ListValue? Next(Rows rows, int numRows, ISpanner.RowEncoding encoding) + { + var row = _lib.Next(ToProto(rows)); + if (row.Values.Count == 0) + { + return null; + } + return row; + } + + public async Task NextAsync(Rows rows, int numRows, ISpanner.RowEncoding encoding) + { + if (_streams.TryGetValue(rows.SpannerConnection.Id, out var stream)) + { + return await NextStreaming(stream, rows); + } + return await Task.Run(() => Next(rows, numRows, encoding)); + } + + private async Task NextStreaming(AsyncDuplexStreamingCall stream, Rows rows) + { + await stream.RequestStream.WriteAsync(new ConnectionStreamRequest + { + NextRequest = new NextRequest + { + Rows = ToProto(rows), + } + }); + await stream.ResponseStream.MoveNext(); + var response = stream.ResponseStream.Current; + if (response.Row.Values.Count == 0) + { + return null; + } + return response.Row; + } + + public void CloseRows(Rows rows) + { + _lib.CloseRows(ToProto(rows)); + } + + public Transaction BeginTransaction(Connection connection, TransactionOptions transactionOptions) + { + var transaction = _lib.BeginTransaction(ToProto(connection), transactionOptions); + return new Transaction(connection, transaction.Id); + } + + public CommitResponse Commit(Connection connection) + { + throw new System.NotImplementedException(); + //return _lib.Commit(ToProto(transaction)); + } + + public void Rollback(Connection connection) + { + throw new System.NotImplementedException(); + //_lib.Rollback(ToProto(transaction)); + } + + private static V1.Pool ToProto(Pool pool) => new() {Id = pool.Id}; + + private static V1.Connection ToProto(Connection connection) => new() {Pool = ToProto(connection.Pool), Id = connection.Id}; + + private static V1.Transaction ToProto(Transaction transaction) => new() {Connection = ToProto(transaction.SpannerConnection), Id = transaction.Id}; + + private static V1.Rows ToProto(Rows rows) => new() {Connection = ToProto(rows.SpannerConnection), Id = rows.Id}; +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/ISpanner.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/ISpanner.cs new file mode 100644 index 00000000..45b9d05e --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/ISpanner.cs @@ -0,0 +1,49 @@ +using System.Threading.Tasks; +using Google.Cloud.Spanner.V1; +using Google.Protobuf.WellKnownTypes; + +namespace Google.Cloud.SpannerLib; + +public interface ISpanner +{ + public enum RowEncoding + { + Proto, + } + + public Pool CreatePool(string dsn); + + public void ClosePool(Pool pool); + + public Connection CreateConnection(Pool pool); + + public void CloseConnection(Connection connection); + + public CommitResponse WriteMutations(Connection connection, BatchWriteRequest.Types.MutationGroup mutations); + + public Rows Execute(Connection connection, ExecuteSqlRequest statement); + + public Task ExecuteAsync(Connection connection, ExecuteSqlRequest statement); + + public long[] ExecuteBatch(Connection connection, ExecuteBatchDmlRequest statements); + + public Task ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequest statements); + + public ResultSetMetadata? Metadata(Rows rows); + + public Task MetadataAsync(Rows rows); + + public ResultSetStats? Stats(Rows rows); + + public ListValue? Next(Rows rows, int numRows, RowEncoding encoding); + + public Task NextAsync(Rows rows, int numRows, RowEncoding encoding); + + public void CloseRows(Rows rows); + + public Transaction BeginTransaction(Connection connection, TransactionOptions transactionOptions); + + public CommitResponse Commit(Connection connection); + + public void Rollback(Connection connection); +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Pool.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Pool.cs new file mode 100644 index 00000000..57554a64 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Pool.cs @@ -0,0 +1,52 @@ +using System; + +namespace Google.Cloud.SpannerLib; + +public class Pool : AbstractLibObject +{ + private static readonly bool DefaultUseNativeLib = true; + private static readonly string? UseNativeLibEnvVar = Environment.GetEnvironmentVariable("USE_NATIVE_LIB"); + private static readonly bool UseNativeLib = + UseNativeLibEnvVar?.Equals("true", StringComparison.InvariantCultureIgnoreCase) ?? DefaultUseNativeLib; + + private static Lazy _spanner = new(CreateSpanner); + + private static ISpanner CreateSpanner() + { + ISpanner spanner; + if (UseNativeLib) + { + spanner = new SharedLibSpanner(); + } + else + { + spanner = new GrpcLibSpanner(); + } + return spanner; + } + + public static Pool Create(string dsn) + { + return Create(_spanner.Value, dsn); + } + + public static Pool Create(ISpanner spanner, string dsn) + { + return spanner.CreatePool(dsn); + } + + internal Pool(ISpanner spanner, long id) : base(spanner, id) + { + } + + public Connection CreateConnection() + { + CheckDisposed(); + return Spanner.CreateConnection(this); + } + + protected override void CloseLibObject() + { + Spanner.ClosePool(this); + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Rows.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Rows.cs new file mode 100644 index 00000000..179928f3 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Rows.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading.Tasks; +using Google.Cloud.Spanner.V1; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; + +namespace Google.Cloud.SpannerLib; + +public class Rows : AbstractLibObject +{ + private readonly Lazy _stats; + + internal Connection SpannerConnection { get; private set; } + + private readonly AsyncServerStreamingCall? _stream; + + public ResultSetMetadata? Metadata { get; private set; } + + private ResultSetStats? Stats => _stats.Value; + + public long UpdateCount + { + get + { + var stats = Stats; + if (stats == null) + { + return -1; + } + if (stats.HasRowCountExact) + { + return (int)stats.RowCountExact; + } + if (stats.HasRowCountLowerBound) + { + return (int)stats.RowCountLowerBound; + } + return -1; + } + } + + internal Rows(Connection connection, long id, bool initMetadata = true) : base(connection.Spanner, id) + { + SpannerConnection = connection; + if (initMetadata) + { + Metadata = Spanner.Metadata(this); + } + _stats = new(() => Spanner.Stats(this)); + _stream = null; + } + + internal Rows(Connection connection, AsyncServerStreamingCall stream) : base(connection.Spanner, -1) + { + SpannerConnection = connection; + _stats = new(() => Spanner.Stats(this)); + _stream = stream; + } + + public async Task InitMetadataAsync() + { + Metadata = await Spanner.MetadataAsync(this); + } + + public ListValue? Next() + { + if (_stream == null) + { + var res = Spanner.Next(this, 1, ISpanner.RowEncoding.Proto); + if (res == null && !_stats.IsValueCreated) + { + // initialize stats. + _ = _stats.Value; + } + return res; + } + if (_stream.ResponseStream.MoveNext().Result) + { + var row = _stream.ResponseStream.Current; + if (row.Rows.Count == 0) + { + return null; + } + Metadata ??= row.Metadata; + return row.Rows[0]; + } + return null; + } + + public async Task NextAsync() + { + if (_stream == null) + { + return await Spanner.NextAsync(this, 1, ISpanner.RowEncoding.Proto); + } + if (await _stream.ResponseStream.MoveNext()) + { + var row = _stream.ResponseStream.Current; + if (row.Rows.Count == 0) + { + return null; + } + Metadata ??= row.Metadata; + return row.Rows[0]; + } + return null; + // var res = await Spanner.NextAsync(this); + // if (res == null && !_stats.IsValueCreated) + // { + // // initialize stats. + // _ = _stats.Value; + // } + // return res; + } + + protected override void CloseLibObject() + { + _stream?.Dispose(); + Spanner.CloseRows(this); + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SharedLibSpanner.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SharedLibSpanner.cs new file mode 100644 index 00000000..e33f9037 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SharedLibSpanner.cs @@ -0,0 +1,185 @@ +using System; +using System.Threading.Tasks; +using Google.Cloud.Spanner.V1; +using Google.Cloud.SpannerLib.Native; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Google.Rpc; + +namespace Google.Cloud.SpannerLib; + +public class SharedLibSpanner : ISpanner +{ + private MessageHandler ExecuteLibraryFunction(Func func) + { + var handler = new MessageHandler(func()); + if (handler.HasError()) + { + try + { + throw CreateException(handler); + } + finally + { + handler.Dispose(); + } + } + return handler; + } + + private SpannerException CreateException(MessageHandler handler) + { + if (handler.Length > 0) + { + var status = Status.Parser.ParseFrom(handler.Value()); + return new SpannerException(status.Code, status.Message); + } + return new SpannerException(handler.Code(), "Unknown error"); + } + + private void ExecuteAndReleaseLibraryFunction(Func func) + { + using var handler = new MessageHandler(func()); + if (handler.HasError()) + { + throw CreateException(handler); + } + } + + public Pool CreatePool(string dsn) + { + using var handler = ExecuteLibraryFunction(() => + { + using var goDsn = new GoString(dsn); + return Native.SpannerLib.CreatePool(goDsn); + }); + return new Pool(this, handler.ObjectId()); + } + + public void ClosePool(Pool pool) + { + ExecuteAndReleaseLibraryFunction(() => Native.SpannerLib.ClosePool(pool.Id)); + } + + public Connection CreateConnection(Pool pool) + { + using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.CreateConnection(pool.Id)); + return new Connection(pool, handler.ObjectId()); + } + + public void CloseConnection(Connection connection) + { + ExecuteAndReleaseLibraryFunction(() => Native.SpannerLib.CloseConnection(connection.Pool.Id, connection.Id)); + } + + public CommitResponse WriteMutations(Connection connection, + BatchWriteRequest.Types.MutationGroup mutations) + { + using var handler = ExecuteLibraryFunction(() => + { + var mutationsBytes = mutations.ToByteArray(); + using var goMutations = DisposableGoSlice.Create(mutationsBytes); + return Native.SpannerLib.WriteMutations(connection.Pool.Id, connection.Id, goMutations.GoSlice); + }); + return CommitResponse.Parser.ParseFrom(handler.Value()); + } + + public Rows Execute(Connection connection, ExecuteSqlRequest statement) + { + using var handler = ExecuteLibraryFunction(() => + { + var statementBytes = statement.ToByteArray(); + using var goStatement = DisposableGoSlice.Create(statementBytes); + return Native.SpannerLib.Execute(connection.Pool.Id, connection.Id, goStatement.GoSlice); + }); + return new Rows(connection, handler.ObjectId()); + } + + public Task ExecuteAsync(Connection connection, ExecuteSqlRequest statement) + { + return Task.Run(() => Execute(connection, statement)); + } + + public long[] ExecuteBatch(Connection connection, ExecuteBatchDmlRequest statements) + { + using var handler = ExecuteLibraryFunction(() => + { + var statementsBytes = statements.ToByteArray(); + using var goStatements = DisposableGoSlice.Create(statementsBytes); + return Native.SpannerLib.ExecuteBatch(connection.Pool.Id, connection.Id, goStatements.GoSlice); + }); + if (handler.Length == 0) + { + return Array.Empty(); + } + + var response = ExecuteBatchDmlResponse.Parser.ParseFrom(handler.Value()); + var result = new long[response.ResultSets.Count]; + for (var i = 0; i < result.Length; i++) + { + result[i] = response.ResultSets[i].Stats.RowCountExact; + } + + return result; + } + + public Task ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequest statements) + { + return Task.Run(() => ExecuteBatch(connection, statements)); + } + + public ResultSetMetadata? Metadata(Rows rows) + { + using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Metadata(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id)); + return handler.Length == 0 ? null : ResultSetMetadata.Parser.ParseFrom(handler.Value()); + } + + public async Task MetadataAsync(Rows rows) + { + return await Task.Run(() => Metadata(rows)); + } + + public ResultSetStats? Stats(Rows rows) + { + using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.ResultSetStats(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id)); + return handler.Length == 0 ? null : ResultSetStats.Parser.ParseFrom(handler.Value()); + } + + public ListValue? Next(Rows rows, int numRows, ISpanner.RowEncoding encoding) + { + using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Next(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id, numRows, (int) encoding)); + return handler.Length == 0 ? null : ListValue.Parser.ParseFrom(handler.Value()); + } + + public async Task NextAsync(Rows rows, int numRows, ISpanner.RowEncoding encoding) + { + return await Task.Run(() => Next(rows, numRows, encoding)); + } + + public void CloseRows(Rows rows) + { + ExecuteAndReleaseLibraryFunction(() => Native.SpannerLib.CloseRows(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id)); + } + + public Transaction BeginTransaction(Connection connection, TransactionOptions transactionOptions) + { + using var handler = ExecuteLibraryFunction(() => + { + var optionsBytes = transactionOptions.ToByteArray(); + using var goOptions = DisposableGoSlice.Create(optionsBytes); + return Native.SpannerLib.BeginTransaction(connection.Pool.Id, connection.Id, goOptions.GoSlice); + }); + return new Transaction(connection, handler.ObjectId()); + } + + public CommitResponse Commit(Connection connection) + { + using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Commit(connection.Pool.Id, connection.Id)); + return CommitResponse.Parser.ParseFrom(handler.Value()); + } + + public void Rollback(Connection connection) + { + ExecuteAndReleaseLibraryFunction(() => Native.SpannerLib.Rollback(connection.Pool.Id, connection.Id)); + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SpannerException.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SpannerException.cs new file mode 100644 index 00000000..966d6f03 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SpannerException.cs @@ -0,0 +1,23 @@ +using System.Data; +using System.Data.Common; + +namespace Google.Cloud.SpannerLib +{ + + public class SpannerException : DbException + { + /// + /// An error code that indicates the general class of problem. + /// + public ErrorCode ErrorCode { get; } + + internal SpannerException(int code, string message) : this((ErrorCode)code, message) + { + } + + internal SpannerException(ErrorCode code, string message) : base(message) + { + ErrorCode = code; + } + } +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Transaction.cs b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Transaction.cs new file mode 100644 index 00000000..90d4e629 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Transaction.cs @@ -0,0 +1,44 @@ +using System; +using Google.Cloud.Spanner.V1; + +namespace Google.Cloud.SpannerLib; + +public class Transaction : AbstractLibObject +{ + internal Connection SpannerConnection { get; private set; } + + internal Transaction(Connection connection, long id) : base(connection.Spanner, id) + { + SpannerConnection = connection; + } + + public Rows Execute(ExecuteSqlRequest statement) + { + return Spanner.Execute(SpannerConnection, statement); + } + + public CommitResponse Commit() + { + MarkDisposed(); + return Spanner.Commit(SpannerConnection); + } + + public void Rollback() + { + MarkDisposed(); + Spanner.Rollback(SpannerConnection); + } + + protected override void CloseLibObject() + { + try + { + Spanner.Rollback(SpannerConnection); + } + catch (Exception) + { + // ignore any exceptions + } + } + +} \ No newline at end of file diff --git a/spannerlib/dotnet-spannerlib/dotnet-spannerlib.sln b/spannerlib/dotnet-spannerlib/dotnet-spannerlib.sln new file mode 100644 index 00000000..44a90049 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/dotnet-spannerlib.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.SpannerLib", "Google.Cloud.SpannerLib\Google.Cloud.SpannerLib.csproj", "{F2E2ED84-6D2B-4063-9E25-B0D0E85F0707}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.SpannerLib.Tests", "Google.Cloud.SpannerLib.Tests\Google.Cloud.SpannerLib.Tests.csproj", "{A5330A7C-2DE8-42A0-A13E-D780EECA72CE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.SpannerLib.Native", "Google.Cloud.SpannerLib.Native\Google.Cloud.SpannerLib.Native.csproj", "{76643D31-564F-48C0-8D94-5128657E83D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.SpannerLib.Grpc", "Google.Cloud.SpannerLib.Grpc\Google.Cloud.SpannerLib.Grpc.csproj", "{9B923838-25CB-461C-9C4F-DAE8F9B942C4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.SpannerLib.GrpcTests", "Google.Cloud.SpannerLib.GrpcTests\Google.Cloud.SpannerLib.GrpcTests.csproj", "{66009437-7B8F-4D10-AC0B-B431AA75CFC5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.SpannerLib.MockServer", "Google.Cloud.SpannerLib.MockServer\Google.Cloud.SpannerLib.MockServer.csproj", "{5849B966-3EF8-4D8F-A514-27ED8A070B3A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F2E2ED84-6D2B-4063-9E25-B0D0E85F0707}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2E2ED84-6D2B-4063-9E25-B0D0E85F0707}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2E2ED84-6D2B-4063-9E25-B0D0E85F0707}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2E2ED84-6D2B-4063-9E25-B0D0E85F0707}.Release|Any CPU.Build.0 = Release|Any CPU + {A5330A7C-2DE8-42A0-A13E-D780EECA72CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5330A7C-2DE8-42A0-A13E-D780EECA72CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5330A7C-2DE8-42A0-A13E-D780EECA72CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5330A7C-2DE8-42A0-A13E-D780EECA72CE}.Release|Any CPU.Build.0 = Release|Any CPU + {76643D31-564F-48C0-8D94-5128657E83D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76643D31-564F-48C0-8D94-5128657E83D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76643D31-564F-48C0-8D94-5128657E83D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76643D31-564F-48C0-8D94-5128657E83D8}.Release|Any CPU.Build.0 = Release|Any CPU + {9B923838-25CB-461C-9C4F-DAE8F9B942C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B923838-25CB-461C-9C4F-DAE8F9B942C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B923838-25CB-461C-9C4F-DAE8F9B942C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B923838-25CB-461C-9C4F-DAE8F9B942C4}.Release|Any CPU.Build.0 = Release|Any CPU + {66009437-7B8F-4D10-AC0B-B431AA75CFC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66009437-7B8F-4D10-AC0B-B431AA75CFC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66009437-7B8F-4D10-AC0B-B431AA75CFC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66009437-7B8F-4D10-AC0B-B431AA75CFC5}.Release|Any CPU.Build.0 = Release|Any CPU + {5849B966-3EF8-4D8F-A514-27ED8A070B3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5849B966-3EF8-4D8F-A514-27ED8A070B3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5849B966-3EF8-4D8F-A514-27ED8A070B3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5849B966-3EF8-4D8F-A514-27ED8A070B3A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/spannerlib/dotnet-spannerlib/global.json b/spannerlib/dotnet-spannerlib/global.json new file mode 100644 index 00000000..93681ff8 --- /dev/null +++ b/spannerlib/dotnet-spannerlib/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "9.0.0", + "rollForward": "latestMinor", + "allowPrerelease": false + } +} \ No newline at end of file diff --git a/spannerlib/go.mod b/spannerlib/go.mod new file mode 100644 index 00000000..58ed3a9c --- /dev/null +++ b/spannerlib/go.mod @@ -0,0 +1,64 @@ +module spannerlib + +go 1.24.0 + +replace github.com/googleapis/go-sql-spanner => .. + +require ( + cloud.google.com/go/spanner v1.85.1 + github.com/google/uuid v1.6.0 + github.com/googleapis/go-sql-spanner v1.17.0 + google.golang.org/api v0.249.0 + google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c + google.golang.org/grpc v1.75.1 + google.golang.org/protobuf v1.36.9 +) + +require ( + cel.dev/expr v0.24.0 // indirect + cloud.google.com/go v0.122.0 // indirect + cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/longrunning v0.6.7 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect + github.com/zeebo/errs v1.4.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.37.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/genproto v0.0.0-20250804133106-a7a43d27e69b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect +) diff --git a/spannerlib/go.sum b/spannerlib/go.sum new file mode 100644 index 00000000..facf0032 --- /dev/null +++ b/spannerlib/go.sum @@ -0,0 +1,1638 @@ +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.122.0 h1:0JTLGrcSIs3HIGsgVPvTx3cfyFSP/k9CI8vLPHTd6Wc= +cloud.google.com/go v0.122.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= +cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/spanner v1.85.1 h1:cJx1ZD//C2QIfFQl8hSTn4twL8amAXtnayyflRIjj40= +cloud.google.com/go/spanner v1.85.1/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +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/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 h1:2afWGsMzkIcN8Qm4mgPJKZWyroE5QBszMiDMYEBrnfw= +github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +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.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +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/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= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +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= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +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/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +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= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +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/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/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/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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-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= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +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/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= +github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= +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= +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= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +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= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.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= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +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/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/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/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +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/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/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/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +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= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +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/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/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +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= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +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/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/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +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= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +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 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/detectors/gcp v1.37.0 h1:B+WbN9RPsvobe6q4vP6KgM8/9plR/HNjgGBrfcOlweA= +go.opentelemetry.io/contrib/detectors/gcp v1.37.0/go.mod h1:K5zQ3TT7p2ru9Qkzk0bKtCql0RGkPj9pRjpXgZJZ+rU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +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= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +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= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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.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/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-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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +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-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +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-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-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= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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.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= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +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= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-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= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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-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= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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-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= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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.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= +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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +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.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= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +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= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/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= +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-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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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.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/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +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.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= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +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/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w= +google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +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-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= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +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-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= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +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-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20250804133106-a7a43d27e69b h1:eZTgydvqZO44zyTZAvMaSyAxccZZdraiSAGvqOczVvk= +google.golang.org/genproto v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:suyz2QBHQKlGIF92HEEsCfO1SwxXdk7PFLz+Zd9Uah4= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/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= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +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.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +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= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +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.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.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +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-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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.v3 v3.0.0-20200313102051-9f266ea9e77c/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= +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= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +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/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +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/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.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/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.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/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +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/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +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= diff --git a/spannerlib/google/spannerlib/v1/spannerlib.pb.go b/spannerlib/google/spannerlib/v1/spannerlib.pb.go new file mode 100644 index 00000000..913681d6 --- /dev/null +++ b/spannerlib/google/spannerlib/v1/spannerlib.pb.go @@ -0,0 +1,1115 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.6 +// protoc v5.29.3 +// source: google/spannerlib/v1/spannerlib.proto + +package spannerlibpb + +import ( + spannerpb "cloud.google.com/go/spanner/apiv1/spannerpb" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreatePoolRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Dsn string `protobuf:"bytes,1,opt,name=dsn,proto3" json:"dsn,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePoolRequest) Reset() { + *x = CreatePoolRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePoolRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePoolRequest) ProtoMessage() {} + +func (x *CreatePoolRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreatePoolRequest.ProtoReflect.Descriptor instead. +func (*CreatePoolRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{0} +} + +func (x *CreatePoolRequest) GetDsn() string { + if x != nil { + return x.Dsn + } + return "" +} + +type CreateConnectionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Pool *Pool `protobuf:"bytes,1,opt,name=pool,proto3" json:"pool,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateConnectionRequest) Reset() { + *x = CreateConnectionRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateConnectionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateConnectionRequest) ProtoMessage() {} + +func (x *CreateConnectionRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateConnectionRequest.ProtoReflect.Descriptor instead. +func (*CreateConnectionRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateConnectionRequest) GetPool() *Pool { + if x != nil { + return x.Pool + } + return nil +} + +type ExecuteRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection *Connection `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + ExecuteSqlRequest *spannerpb.ExecuteSqlRequest `protobuf:"bytes,2,opt,name=execute_sql_request,json=executeSqlRequest,proto3" json:"execute_sql_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ExecuteRequest) Reset() { + *x = ExecuteRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExecuteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteRequest) ProtoMessage() {} + +func (x *ExecuteRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteRequest.ProtoReflect.Descriptor instead. +func (*ExecuteRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{2} +} + +func (x *ExecuteRequest) GetConnection() *Connection { + if x != nil { + return x.Connection + } + return nil +} + +func (x *ExecuteRequest) GetExecuteSqlRequest() *spannerpb.ExecuteSqlRequest { + if x != nil { + return x.ExecuteSqlRequest + } + return nil +} + +type ExecuteTransactionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Transaction *Transaction `protobuf:"bytes,1,opt,name=transaction,proto3" json:"transaction,omitempty"` + ExecuteSqlRequest *spannerpb.ExecuteSqlRequest `protobuf:"bytes,2,opt,name=execute_sql_request,json=executeSqlRequest,proto3" json:"execute_sql_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ExecuteTransactionRequest) Reset() { + *x = ExecuteTransactionRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExecuteTransactionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteTransactionRequest) ProtoMessage() {} + +func (x *ExecuteTransactionRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteTransactionRequest.ProtoReflect.Descriptor instead. +func (*ExecuteTransactionRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{3} +} + +func (x *ExecuteTransactionRequest) GetTransaction() *Transaction { + if x != nil { + return x.Transaction + } + return nil +} + +func (x *ExecuteTransactionRequest) GetExecuteSqlRequest() *spannerpb.ExecuteSqlRequest { + if x != nil { + return x.ExecuteSqlRequest + } + return nil +} + +type ExecuteBatchDmlRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection *Connection `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + ExecuteBatchDmlRequest *spannerpb.ExecuteBatchDmlRequest `protobuf:"bytes,2,opt,name=execute_batch_dml_request,json=executeBatchDmlRequest,proto3" json:"execute_batch_dml_request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ExecuteBatchDmlRequest) Reset() { + *x = ExecuteBatchDmlRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExecuteBatchDmlRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteBatchDmlRequest) ProtoMessage() {} + +func (x *ExecuteBatchDmlRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteBatchDmlRequest.ProtoReflect.Descriptor instead. +func (*ExecuteBatchDmlRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{4} +} + +func (x *ExecuteBatchDmlRequest) GetConnection() *Connection { + if x != nil { + return x.Connection + } + return nil +} + +func (x *ExecuteBatchDmlRequest) GetExecuteBatchDmlRequest() *spannerpb.ExecuteBatchDmlRequest { + if x != nil { + return x.ExecuteBatchDmlRequest + } + return nil +} + +type BeginTransactionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection *Connection `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + TransactionOptions *spannerpb.TransactionOptions `protobuf:"bytes,2,opt,name=transaction_options,json=transactionOptions,proto3" json:"transaction_options,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BeginTransactionRequest) Reset() { + *x = BeginTransactionRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BeginTransactionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BeginTransactionRequest) ProtoMessage() {} + +func (x *BeginTransactionRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BeginTransactionRequest.ProtoReflect.Descriptor instead. +func (*BeginTransactionRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{5} +} + +func (x *BeginTransactionRequest) GetConnection() *Connection { + if x != nil { + return x.Connection + } + return nil +} + +func (x *BeginTransactionRequest) GetTransactionOptions() *spannerpb.TransactionOptions { + if x != nil { + return x.TransactionOptions + } + return nil +} + +type ApplyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection *Connection `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + Mutations *spannerpb.BatchWriteRequest_MutationGroup `protobuf:"bytes,2,opt,name=mutations,proto3" json:"mutations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ApplyRequest) Reset() { + *x = ApplyRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ApplyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyRequest) ProtoMessage() {} + +func (x *ApplyRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead. +func (*ApplyRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{6} +} + +func (x *ApplyRequest) GetConnection() *Connection { + if x != nil { + return x.Connection + } + return nil +} + +func (x *ApplyRequest) GetMutations() *spannerpb.BatchWriteRequest_MutationGroup { + if x != nil { + return x.Mutations + } + return nil +} + +type BufferWriteRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Transaction *Transaction `protobuf:"bytes,1,opt,name=transaction,proto3" json:"transaction,omitempty"` + Mutations *spannerpb.BatchWriteRequest_MutationGroup `protobuf:"bytes,2,opt,name=mutations,proto3" json:"mutations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BufferWriteRequest) Reset() { + *x = BufferWriteRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BufferWriteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BufferWriteRequest) ProtoMessage() {} + +func (x *BufferWriteRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BufferWriteRequest.ProtoReflect.Descriptor instead. +func (*BufferWriteRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{7} +} + +func (x *BufferWriteRequest) GetTransaction() *Transaction { + if x != nil { + return x.Transaction + } + return nil +} + +func (x *BufferWriteRequest) GetMutations() *spannerpb.BatchWriteRequest_MutationGroup { + if x != nil { + return x.Mutations + } + return nil +} + +type Pool struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Pool) Reset() { + *x = Pool{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Pool) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pool) ProtoMessage() {} + +func (x *Pool) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pool.ProtoReflect.Descriptor instead. +func (*Pool) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{8} +} + +func (x *Pool) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +type Connection struct { + state protoimpl.MessageState `protogen:"open.v1"` + Pool *Pool `protobuf:"bytes,1,opt,name=pool,proto3" json:"pool,omitempty"` + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Connection) Reset() { + *x = Connection{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Connection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Connection) ProtoMessage() {} + +func (x *Connection) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Connection.ProtoReflect.Descriptor instead. +func (*Connection) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{9} +} + +func (x *Connection) GetPool() *Pool { + if x != nil { + return x.Pool + } + return nil +} + +func (x *Connection) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +type Rows struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection *Connection `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Rows) Reset() { + *x = Rows{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Rows) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Rows) ProtoMessage() {} + +func (x *Rows) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Rows.ProtoReflect.Descriptor instead. +func (*Rows) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{10} +} + +func (x *Rows) GetConnection() *Connection { + if x != nil { + return x.Connection + } + return nil +} + +func (x *Rows) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +type NextRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Rows *Rows `protobuf:"bytes,1,opt,name=rows,proto3" json:"rows,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NextRequest) Reset() { + *x = NextRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NextRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NextRequest) ProtoMessage() {} + +func (x *NextRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NextRequest.ProtoReflect.Descriptor instead. +func (*NextRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{11} +} + +func (x *NextRequest) GetRows() *Rows { + if x != nil { + return x.Rows + } + return nil +} + +type MetadataRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Rows *Rows `protobuf:"bytes,1,opt,name=rows,proto3" json:"rows,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MetadataRequest) Reset() { + *x = MetadataRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MetadataRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MetadataRequest) ProtoMessage() {} + +func (x *MetadataRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MetadataRequest.ProtoReflect.Descriptor instead. +func (*MetadataRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{12} +} + +func (x *MetadataRequest) GetRows() *Rows { + if x != nil { + return x.Rows + } + return nil +} + +type ResultSetStatsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Rows *Rows `protobuf:"bytes,1,opt,name=rows,proto3" json:"rows,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResultSetStatsRequest) Reset() { + *x = ResultSetStatsRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResultSetStatsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResultSetStatsRequest) ProtoMessage() {} + +func (x *ResultSetStatsRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResultSetStatsRequest.ProtoReflect.Descriptor instead. +func (*ResultSetStatsRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{13} +} + +func (x *ResultSetStatsRequest) GetRows() *Rows { + if x != nil { + return x.Rows + } + return nil +} + +type Transaction struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection *Connection `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Transaction) Reset() { + *x = Transaction{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Transaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction) ProtoMessage() {} + +func (x *Transaction) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction.ProtoReflect.Descriptor instead. +func (*Transaction) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{14} +} + +func (x *Transaction) GetConnection() *Connection { + if x != nil { + return x.Connection + } + return nil +} + +func (x *Transaction) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +type ConnectionStreamRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Request: + // + // *ConnectionStreamRequest_ExecuteRequest + Request isConnectionStreamRequest_Request `protobuf_oneof:"request"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConnectionStreamRequest) Reset() { + *x = ConnectionStreamRequest{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConnectionStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectionStreamRequest) ProtoMessage() {} + +func (x *ConnectionStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectionStreamRequest.ProtoReflect.Descriptor instead. +func (*ConnectionStreamRequest) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{15} +} + +func (x *ConnectionStreamRequest) GetRequest() isConnectionStreamRequest_Request { + if x != nil { + return x.Request + } + return nil +} + +func (x *ConnectionStreamRequest) GetExecuteRequest() *ExecuteRequest { + if x != nil { + if x, ok := x.Request.(*ConnectionStreamRequest_ExecuteRequest); ok { + return x.ExecuteRequest + } + } + return nil +} + +type isConnectionStreamRequest_Request interface { + isConnectionStreamRequest_Request() +} + +type ConnectionStreamRequest_ExecuteRequest struct { + ExecuteRequest *ExecuteRequest `protobuf:"bytes,1,opt,name=execute_request,json=executeRequest,proto3,oneof"` +} + +func (*ConnectionStreamRequest_ExecuteRequest) isConnectionStreamRequest_Request() {} + +type ConnectionStreamResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Response: + // + // *ConnectionStreamResponse_Row + Response isConnectionStreamResponse_Response `protobuf_oneof:"response"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConnectionStreamResponse) Reset() { + *x = ConnectionStreamResponse{} + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConnectionStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectionStreamResponse) ProtoMessage() {} + +func (x *ConnectionStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_google_spannerlib_v1_spannerlib_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectionStreamResponse.ProtoReflect.Descriptor instead. +func (*ConnectionStreamResponse) Descriptor() ([]byte, []int) { + return file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP(), []int{16} +} + +func (x *ConnectionStreamResponse) GetResponse() isConnectionStreamResponse_Response { + if x != nil { + return x.Response + } + return nil +} + +func (x *ConnectionStreamResponse) GetRow() *spannerpb.PartialResultSet { + if x != nil { + if x, ok := x.Response.(*ConnectionStreamResponse_Row); ok { + return x.Row + } + } + return nil +} + +type isConnectionStreamResponse_Response interface { + isConnectionStreamResponse_Response() +} + +type ConnectionStreamResponse_Row struct { + Row *spannerpb.PartialResultSet `protobuf:"bytes,1,opt,name=row,proto3,oneof"` +} + +func (*ConnectionStreamResponse_Row) isConnectionStreamResponse_Response() {} + +var File_google_spannerlib_v1_spannerlib_proto protoreflect.FileDescriptor + +const file_google_spannerlib_v1_spannerlib_proto_rawDesc = "" + + "\n" + + "%google/spannerlib/v1/spannerlib.proto\x12\x14google.spannerlib.v1\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a google/spanner/v1/mutation.proto\x1a\"google/spanner/v1/result_set.proto\x1a\x1fgoogle/spanner/v1/spanner.proto\x1a#google/spanner/v1/transaction.proto\"*\n" + + "\x11CreatePoolRequest\x12\x15\n" + + "\x03dsn\x18\x01 \x01(\tB\x03\xe0A\x02R\x03dsn\"N\n" + + "\x17CreateConnectionRequest\x123\n" + + "\x04pool\x18\x01 \x01(\v2\x1a.google.spannerlib.v1.PoolB\x03\xe0A\x02R\x04pool\"\xb2\x01\n" + + "\x0eExecuteRequest\x12E\n" + + "\n" + + "connection\x18\x01 \x01(\v2 .google.spannerlib.v1.ConnectionB\x03\xe0A\x02R\n" + + "connection\x12Y\n" + + "\x13execute_sql_request\x18\x02 \x01(\v2$.google.spanner.v1.ExecuteSqlRequestB\x03\xe0A\x02R\x11executeSqlRequest\"\xc0\x01\n" + + "\x19ExecuteTransactionRequest\x12H\n" + + "\vtransaction\x18\x01 \x01(\v2!.google.spannerlib.v1.TransactionB\x03\xe0A\x02R\vtransaction\x12Y\n" + + "\x13execute_sql_request\x18\x02 \x01(\v2$.google.spanner.v1.ExecuteSqlRequestB\x03\xe0A\x02R\x11executeSqlRequest\"\xca\x01\n" + + "\x16ExecuteBatchDmlRequest\x12E\n" + + "\n" + + "connection\x18\x01 \x01(\v2 .google.spannerlib.v1.ConnectionB\x03\xe0A\x02R\n" + + "connection\x12i\n" + + "\x19execute_batch_dml_request\x18\x02 \x01(\v2).google.spanner.v1.ExecuteBatchDmlRequestB\x03\xe0A\x02R\x16executeBatchDmlRequest\"\xbd\x01\n" + + "\x17BeginTransactionRequest\x12E\n" + + "\n" + + "connection\x18\x01 \x01(\v2 .google.spannerlib.v1.ConnectionB\x03\xe0A\x02R\n" + + "connection\x12[\n" + + "\x13transaction_options\x18\x02 \x01(\v2%.google.spanner.v1.TransactionOptionsB\x03\xe0A\x02R\x12transactionOptions\"\xac\x01\n" + + "\fApplyRequest\x12E\n" + + "\n" + + "connection\x18\x01 \x01(\v2 .google.spannerlib.v1.ConnectionB\x03\xe0A\x02R\n" + + "connection\x12U\n" + + "\tmutations\x18\x02 \x01(\v22.google.spanner.v1.BatchWriteRequest.MutationGroupB\x03\xe0A\x02R\tmutations\"\xb5\x01\n" + + "\x12BufferWriteRequest\x12H\n" + + "\vtransaction\x18\x01 \x01(\v2!.google.spannerlib.v1.TransactionB\x03\xe0A\x02R\vtransaction\x12U\n" + + "\tmutations\x18\x02 \x01(\v22.google.spanner.v1.BatchWriteRequest.MutationGroupB\x03\xe0A\x02R\tmutations\"\x1b\n" + + "\x04Pool\x12\x13\n" + + "\x02id\x18\x01 \x01(\x03B\x03\xe0A\x02R\x02id\"V\n" + + "\n" + + "Connection\x123\n" + + "\x04pool\x18\x01 \x01(\v2\x1a.google.spannerlib.v1.PoolB\x03\xe0A\x02R\x04pool\x12\x13\n" + + "\x02id\x18\x02 \x01(\x03B\x03\xe0A\x02R\x02id\"b\n" + + "\x04Rows\x12E\n" + + "\n" + + "connection\x18\x01 \x01(\v2 .google.spannerlib.v1.ConnectionB\x03\xe0A\x02R\n" + + "connection\x12\x13\n" + + "\x02id\x18\x02 \x01(\x03B\x03\xe0A\x02R\x02id\"B\n" + + "\vNextRequest\x123\n" + + "\x04rows\x18\x01 \x01(\v2\x1a.google.spannerlib.v1.RowsB\x03\xe0A\x02R\x04rows\"F\n" + + "\x0fMetadataRequest\x123\n" + + "\x04rows\x18\x01 \x01(\v2\x1a.google.spannerlib.v1.RowsB\x03\xe0A\x02R\x04rows\"L\n" + + "\x15ResultSetStatsRequest\x123\n" + + "\x04rows\x18\x01 \x01(\v2\x1a.google.spannerlib.v1.RowsB\x03\xe0A\x02R\x04rows\"i\n" + + "\vTransaction\x12E\n" + + "\n" + + "connection\x18\x01 \x01(\v2 .google.spannerlib.v1.ConnectionB\x03\xe0A\x02R\n" + + "connection\x12\x13\n" + + "\x02id\x18\x02 \x01(\x03B\x03\xe0A\x02R\x02id\"u\n" + + "\x17ConnectionStreamRequest\x12O\n" + + "\x0fexecute_request\x18\x01 \x01(\v2$.google.spannerlib.v1.ExecuteRequestH\x00R\x0eexecuteRequestB\t\n" + + "\arequest\"_\n" + + "\x18ConnectionStreamResponse\x127\n" + + "\x03row\x18\x01 \x01(\v2#.google.spanner.v1.PartialResultSetH\x00R\x03rowB\n" + + "\n" + + "\bresponse2\xa9\f\n" + + "\n" + + "SpannerLib\x12S\n" + + "\n" + + "CreatePool\x12'.google.spannerlib.v1.CreatePoolRequest\x1a\x1a.google.spannerlib.v1.Pool\"\x00\x12A\n" + + "\tClosePool\x12\x1a.google.spannerlib.v1.Pool\x1a\x16.google.protobuf.Empty\"\x00\x12e\n" + + "\x10CreateConnection\x12-.google.spannerlib.v1.CreateConnectionRequest\x1a .google.spannerlib.v1.Connection\"\x00\x12M\n" + + "\x0fCloseConnection\x12 .google.spannerlib.v1.Connection\x1a\x16.google.protobuf.Empty\"\x00\x12M\n" + + "\aExecute\x12$.google.spannerlib.v1.ExecuteRequest\x1a\x1a.google.spannerlib.v1.Rows\"\x00\x12a\n" + + "\x10ExecuteStreaming\x12$.google.spannerlib.v1.ExecuteRequest\x1a#.google.spanner.v1.PartialResultSet\"\x000\x01\x12c\n" + + "\x12ExecuteTransaction\x12/.google.spannerlib.v1.ExecuteTransactionRequest\x1a\x1a.google.spannerlib.v1.Rows\"\x00\x12m\n" + + "\x0fExecuteBatchDml\x12,.google.spannerlib.v1.ExecuteBatchDmlRequest\x1a*.google.spanner.v1.ExecuteBatchDmlResponse\"\x00\x12N\n" + + "\bMetadata\x12\x1a.google.spannerlib.v1.Rows\x1a$.google.spanner.v1.ResultSetMetadata\"\x00\x12@\n" + + "\x04Next\x12\x1a.google.spannerlib.v1.Rows\x1a\x1a.google.protobuf.ListValue\"\x00\x12Q\n" + + "\x0eResultSetStats\x12\x1a.google.spannerlib.v1.Rows\x1a!.google.spanner.v1.ResultSetStats\"\x00\x12A\n" + + "\tCloseRows\x12\x1a.google.spannerlib.v1.Rows\x1a\x16.google.protobuf.Empty\"\x00\x12f\n" + + "\x10BeginTransaction\x12-.google.spannerlib.v1.BeginTransactionRequest\x1a!.google.spannerlib.v1.Transaction\"\x00\x12P\n" + + "\x06Commit\x12!.google.spannerlib.v1.Transaction\x1a!.google.spanner.v1.CommitResponse\"\x00\x12G\n" + + "\bRollback\x12!.google.spannerlib.v1.Transaction\x1a\x16.google.protobuf.Empty\"\x00\x12P\n" + + "\x05Apply\x12\".google.spannerlib.v1.ApplyRequest\x1a!.google.spanner.v1.CommitResponse\"\x00\x12Q\n" + + "\vBufferWrite\x12(.google.spannerlib.v1.BufferWriteRequest\x1a\x16.google.protobuf.Empty\"\x00\x12w\n" + + "\x10ConnectionStream\x12-.google.spannerlib.v1.ConnectionStreamRequest\x1a..google.spannerlib.v1.ConnectionStreamResponse\"\x00(\x010\x01B\xc7\x01\n" + + "\x18com.google.spannerlib.v1B\x0fSpannerLibProtoP\x01Z>cloud.google.com/go/spannerlib/apiv1/spannerlibpb;spannerlibpb\xaa\x02\x1aGoogle.Cloud.SpannerLib.V1\xca\x02\x1aGoogle\\Cloud\\SpannerLib\\V1\xea\x02\x1dGoogle::Cloud::SpannerLib::V1b\x06proto3" + +var ( + file_google_spannerlib_v1_spannerlib_proto_rawDescOnce sync.Once + file_google_spannerlib_v1_spannerlib_proto_rawDescData []byte +) + +func file_google_spannerlib_v1_spannerlib_proto_rawDescGZIP() []byte { + file_google_spannerlib_v1_spannerlib_proto_rawDescOnce.Do(func() { + file_google_spannerlib_v1_spannerlib_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_google_spannerlib_v1_spannerlib_proto_rawDesc), len(file_google_spannerlib_v1_spannerlib_proto_rawDesc))) + }) + return file_google_spannerlib_v1_spannerlib_proto_rawDescData +} + +var file_google_spannerlib_v1_spannerlib_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_google_spannerlib_v1_spannerlib_proto_goTypes = []any{ + (*CreatePoolRequest)(nil), // 0: google.spannerlib.v1.CreatePoolRequest + (*CreateConnectionRequest)(nil), // 1: google.spannerlib.v1.CreateConnectionRequest + (*ExecuteRequest)(nil), // 2: google.spannerlib.v1.ExecuteRequest + (*ExecuteTransactionRequest)(nil), // 3: google.spannerlib.v1.ExecuteTransactionRequest + (*ExecuteBatchDmlRequest)(nil), // 4: google.spannerlib.v1.ExecuteBatchDmlRequest + (*BeginTransactionRequest)(nil), // 5: google.spannerlib.v1.BeginTransactionRequest + (*ApplyRequest)(nil), // 6: google.spannerlib.v1.ApplyRequest + (*BufferWriteRequest)(nil), // 7: google.spannerlib.v1.BufferWriteRequest + (*Pool)(nil), // 8: google.spannerlib.v1.Pool + (*Connection)(nil), // 9: google.spannerlib.v1.Connection + (*Rows)(nil), // 10: google.spannerlib.v1.Rows + (*NextRequest)(nil), // 11: google.spannerlib.v1.NextRequest + (*MetadataRequest)(nil), // 12: google.spannerlib.v1.MetadataRequest + (*ResultSetStatsRequest)(nil), // 13: google.spannerlib.v1.ResultSetStatsRequest + (*Transaction)(nil), // 14: google.spannerlib.v1.Transaction + (*ConnectionStreamRequest)(nil), // 15: google.spannerlib.v1.ConnectionStreamRequest + (*ConnectionStreamResponse)(nil), // 16: google.spannerlib.v1.ConnectionStreamResponse + (*spannerpb.ExecuteSqlRequest)(nil), // 17: google.spanner.v1.ExecuteSqlRequest + (*spannerpb.ExecuteBatchDmlRequest)(nil), // 18: google.spanner.v1.ExecuteBatchDmlRequest + (*spannerpb.TransactionOptions)(nil), // 19: google.spanner.v1.TransactionOptions + (*spannerpb.BatchWriteRequest_MutationGroup)(nil), // 20: google.spanner.v1.BatchWriteRequest.MutationGroup + (*spannerpb.PartialResultSet)(nil), // 21: google.spanner.v1.PartialResultSet + (*emptypb.Empty)(nil), // 22: google.protobuf.Empty + (*spannerpb.ExecuteBatchDmlResponse)(nil), // 23: google.spanner.v1.ExecuteBatchDmlResponse + (*spannerpb.ResultSetMetadata)(nil), // 24: google.spanner.v1.ResultSetMetadata + (*structpb.ListValue)(nil), // 25: google.protobuf.ListValue + (*spannerpb.ResultSetStats)(nil), // 26: google.spanner.v1.ResultSetStats + (*spannerpb.CommitResponse)(nil), // 27: google.spanner.v1.CommitResponse +} +var file_google_spannerlib_v1_spannerlib_proto_depIdxs = []int32{ + 8, // 0: google.spannerlib.v1.CreateConnectionRequest.pool:type_name -> google.spannerlib.v1.Pool + 9, // 1: google.spannerlib.v1.ExecuteRequest.connection:type_name -> google.spannerlib.v1.Connection + 17, // 2: google.spannerlib.v1.ExecuteRequest.execute_sql_request:type_name -> google.spanner.v1.ExecuteSqlRequest + 14, // 3: google.spannerlib.v1.ExecuteTransactionRequest.transaction:type_name -> google.spannerlib.v1.Transaction + 17, // 4: google.spannerlib.v1.ExecuteTransactionRequest.execute_sql_request:type_name -> google.spanner.v1.ExecuteSqlRequest + 9, // 5: google.spannerlib.v1.ExecuteBatchDmlRequest.connection:type_name -> google.spannerlib.v1.Connection + 18, // 6: google.spannerlib.v1.ExecuteBatchDmlRequest.execute_batch_dml_request:type_name -> google.spanner.v1.ExecuteBatchDmlRequest + 9, // 7: google.spannerlib.v1.BeginTransactionRequest.connection:type_name -> google.spannerlib.v1.Connection + 19, // 8: google.spannerlib.v1.BeginTransactionRequest.transaction_options:type_name -> google.spanner.v1.TransactionOptions + 9, // 9: google.spannerlib.v1.ApplyRequest.connection:type_name -> google.spannerlib.v1.Connection + 20, // 10: google.spannerlib.v1.ApplyRequest.mutations:type_name -> google.spanner.v1.BatchWriteRequest.MutationGroup + 14, // 11: google.spannerlib.v1.BufferWriteRequest.transaction:type_name -> google.spannerlib.v1.Transaction + 20, // 12: google.spannerlib.v1.BufferWriteRequest.mutations:type_name -> google.spanner.v1.BatchWriteRequest.MutationGroup + 8, // 13: google.spannerlib.v1.Connection.pool:type_name -> google.spannerlib.v1.Pool + 9, // 14: google.spannerlib.v1.Rows.connection:type_name -> google.spannerlib.v1.Connection + 10, // 15: google.spannerlib.v1.NextRequest.rows:type_name -> google.spannerlib.v1.Rows + 10, // 16: google.spannerlib.v1.MetadataRequest.rows:type_name -> google.spannerlib.v1.Rows + 10, // 17: google.spannerlib.v1.ResultSetStatsRequest.rows:type_name -> google.spannerlib.v1.Rows + 9, // 18: google.spannerlib.v1.Transaction.connection:type_name -> google.spannerlib.v1.Connection + 2, // 19: google.spannerlib.v1.ConnectionStreamRequest.execute_request:type_name -> google.spannerlib.v1.ExecuteRequest + 21, // 20: google.spannerlib.v1.ConnectionStreamResponse.row:type_name -> google.spanner.v1.PartialResultSet + 0, // 21: google.spannerlib.v1.SpannerLib.CreatePool:input_type -> google.spannerlib.v1.CreatePoolRequest + 8, // 22: google.spannerlib.v1.SpannerLib.ClosePool:input_type -> google.spannerlib.v1.Pool + 1, // 23: google.spannerlib.v1.SpannerLib.CreateConnection:input_type -> google.spannerlib.v1.CreateConnectionRequest + 9, // 24: google.spannerlib.v1.SpannerLib.CloseConnection:input_type -> google.spannerlib.v1.Connection + 2, // 25: google.spannerlib.v1.SpannerLib.Execute:input_type -> google.spannerlib.v1.ExecuteRequest + 2, // 26: google.spannerlib.v1.SpannerLib.ExecuteStreaming:input_type -> google.spannerlib.v1.ExecuteRequest + 3, // 27: google.spannerlib.v1.SpannerLib.ExecuteTransaction:input_type -> google.spannerlib.v1.ExecuteTransactionRequest + 4, // 28: google.spannerlib.v1.SpannerLib.ExecuteBatchDml:input_type -> google.spannerlib.v1.ExecuteBatchDmlRequest + 10, // 29: google.spannerlib.v1.SpannerLib.Metadata:input_type -> google.spannerlib.v1.Rows + 10, // 30: google.spannerlib.v1.SpannerLib.Next:input_type -> google.spannerlib.v1.Rows + 10, // 31: google.spannerlib.v1.SpannerLib.ResultSetStats:input_type -> google.spannerlib.v1.Rows + 10, // 32: google.spannerlib.v1.SpannerLib.CloseRows:input_type -> google.spannerlib.v1.Rows + 5, // 33: google.spannerlib.v1.SpannerLib.BeginTransaction:input_type -> google.spannerlib.v1.BeginTransactionRequest + 14, // 34: google.spannerlib.v1.SpannerLib.Commit:input_type -> google.spannerlib.v1.Transaction + 14, // 35: google.spannerlib.v1.SpannerLib.Rollback:input_type -> google.spannerlib.v1.Transaction + 6, // 36: google.spannerlib.v1.SpannerLib.Apply:input_type -> google.spannerlib.v1.ApplyRequest + 7, // 37: google.spannerlib.v1.SpannerLib.BufferWrite:input_type -> google.spannerlib.v1.BufferWriteRequest + 15, // 38: google.spannerlib.v1.SpannerLib.ConnectionStream:input_type -> google.spannerlib.v1.ConnectionStreamRequest + 8, // 39: google.spannerlib.v1.SpannerLib.CreatePool:output_type -> google.spannerlib.v1.Pool + 22, // 40: google.spannerlib.v1.SpannerLib.ClosePool:output_type -> google.protobuf.Empty + 9, // 41: google.spannerlib.v1.SpannerLib.CreateConnection:output_type -> google.spannerlib.v1.Connection + 22, // 42: google.spannerlib.v1.SpannerLib.CloseConnection:output_type -> google.protobuf.Empty + 10, // 43: google.spannerlib.v1.SpannerLib.Execute:output_type -> google.spannerlib.v1.Rows + 21, // 44: google.spannerlib.v1.SpannerLib.ExecuteStreaming:output_type -> google.spanner.v1.PartialResultSet + 10, // 45: google.spannerlib.v1.SpannerLib.ExecuteTransaction:output_type -> google.spannerlib.v1.Rows + 23, // 46: google.spannerlib.v1.SpannerLib.ExecuteBatchDml:output_type -> google.spanner.v1.ExecuteBatchDmlResponse + 24, // 47: google.spannerlib.v1.SpannerLib.Metadata:output_type -> google.spanner.v1.ResultSetMetadata + 25, // 48: google.spannerlib.v1.SpannerLib.Next:output_type -> google.protobuf.ListValue + 26, // 49: google.spannerlib.v1.SpannerLib.ResultSetStats:output_type -> google.spanner.v1.ResultSetStats + 22, // 50: google.spannerlib.v1.SpannerLib.CloseRows:output_type -> google.protobuf.Empty + 14, // 51: google.spannerlib.v1.SpannerLib.BeginTransaction:output_type -> google.spannerlib.v1.Transaction + 27, // 52: google.spannerlib.v1.SpannerLib.Commit:output_type -> google.spanner.v1.CommitResponse + 22, // 53: google.spannerlib.v1.SpannerLib.Rollback:output_type -> google.protobuf.Empty + 27, // 54: google.spannerlib.v1.SpannerLib.Apply:output_type -> google.spanner.v1.CommitResponse + 22, // 55: google.spannerlib.v1.SpannerLib.BufferWrite:output_type -> google.protobuf.Empty + 16, // 56: google.spannerlib.v1.SpannerLib.ConnectionStream:output_type -> google.spannerlib.v1.ConnectionStreamResponse + 39, // [39:57] is the sub-list for method output_type + 21, // [21:39] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name +} + +func init() { file_google_spannerlib_v1_spannerlib_proto_init() } +func file_google_spannerlib_v1_spannerlib_proto_init() { + if File_google_spannerlib_v1_spannerlib_proto != nil { + return + } + file_google_spannerlib_v1_spannerlib_proto_msgTypes[15].OneofWrappers = []any{ + (*ConnectionStreamRequest_ExecuteRequest)(nil), + } + file_google_spannerlib_v1_spannerlib_proto_msgTypes[16].OneofWrappers = []any{ + (*ConnectionStreamResponse_Row)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_google_spannerlib_v1_spannerlib_proto_rawDesc), len(file_google_spannerlib_v1_spannerlib_proto_rawDesc)), + NumEnums: 0, + NumMessages: 17, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_google_spannerlib_v1_spannerlib_proto_goTypes, + DependencyIndexes: file_google_spannerlib_v1_spannerlib_proto_depIdxs, + MessageInfos: file_google_spannerlib_v1_spannerlib_proto_msgTypes, + }.Build() + File_google_spannerlib_v1_spannerlib_proto = out.File + file_google_spannerlib_v1_spannerlib_proto_goTypes = nil + file_google_spannerlib_v1_spannerlib_proto_depIdxs = nil +} diff --git a/spannerlib/google/spannerlib/v1/spannerlib_grpc.pb.go b/spannerlib/google/spannerlib/v1/spannerlib_grpc.pb.go new file mode 100644 index 00000000..fa0dee68 --- /dev/null +++ b/spannerlib/google/spannerlib/v1/spannerlib_grpc.pb.go @@ -0,0 +1,768 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.3 +// source: google/spannerlib/v1/spannerlib.proto + +package spannerlibpb + +import ( + spannerpb "cloud.google.com/go/spanner/apiv1/spannerpb" + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" + structpb "google.golang.org/protobuf/types/known/structpb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + SpannerLib_CreatePool_FullMethodName = "/google.spannerlib.v1.SpannerLib/CreatePool" + SpannerLib_ClosePool_FullMethodName = "/google.spannerlib.v1.SpannerLib/ClosePool" + SpannerLib_CreateConnection_FullMethodName = "/google.spannerlib.v1.SpannerLib/CreateConnection" + SpannerLib_CloseConnection_FullMethodName = "/google.spannerlib.v1.SpannerLib/CloseConnection" + SpannerLib_Execute_FullMethodName = "/google.spannerlib.v1.SpannerLib/Execute" + SpannerLib_ExecuteStreaming_FullMethodName = "/google.spannerlib.v1.SpannerLib/ExecuteStreaming" + SpannerLib_ExecuteTransaction_FullMethodName = "/google.spannerlib.v1.SpannerLib/ExecuteTransaction" + SpannerLib_ExecuteBatchDml_FullMethodName = "/google.spannerlib.v1.SpannerLib/ExecuteBatchDml" + SpannerLib_Metadata_FullMethodName = "/google.spannerlib.v1.SpannerLib/Metadata" + SpannerLib_Next_FullMethodName = "/google.spannerlib.v1.SpannerLib/Next" + SpannerLib_ResultSetStats_FullMethodName = "/google.spannerlib.v1.SpannerLib/ResultSetStats" + SpannerLib_CloseRows_FullMethodName = "/google.spannerlib.v1.SpannerLib/CloseRows" + SpannerLib_BeginTransaction_FullMethodName = "/google.spannerlib.v1.SpannerLib/BeginTransaction" + SpannerLib_Commit_FullMethodName = "/google.spannerlib.v1.SpannerLib/Commit" + SpannerLib_Rollback_FullMethodName = "/google.spannerlib.v1.SpannerLib/Rollback" + SpannerLib_Apply_FullMethodName = "/google.spannerlib.v1.SpannerLib/Apply" + SpannerLib_BufferWrite_FullMethodName = "/google.spannerlib.v1.SpannerLib/BufferWrite" + SpannerLib_ConnectionStream_FullMethodName = "/google.spannerlib.v1.SpannerLib/ConnectionStream" +) + +// SpannerLibClient is the client API for SpannerLib service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type SpannerLibClient interface { + CreatePool(ctx context.Context, in *CreatePoolRequest, opts ...grpc.CallOption) (*Pool, error) + ClosePool(ctx context.Context, in *Pool, opts ...grpc.CallOption) (*emptypb.Empty, error) + CreateConnection(ctx context.Context, in *CreateConnectionRequest, opts ...grpc.CallOption) (*Connection, error) + CloseConnection(ctx context.Context, in *Connection, opts ...grpc.CallOption) (*emptypb.Empty, error) + Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*Rows, error) + ExecuteStreaming(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[spannerpb.PartialResultSet], error) + ExecuteTransaction(ctx context.Context, in *ExecuteTransactionRequest, opts ...grpc.CallOption) (*Rows, error) + ExecuteBatchDml(ctx context.Context, in *ExecuteBatchDmlRequest, opts ...grpc.CallOption) (*spannerpb.ExecuteBatchDmlResponse, error) + Metadata(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*spannerpb.ResultSetMetadata, error) + Next(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*structpb.ListValue, error) + ResultSetStats(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*spannerpb.ResultSetStats, error) + CloseRows(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*emptypb.Empty, error) + BeginTransaction(ctx context.Context, in *BeginTransactionRequest, opts ...grpc.CallOption) (*Transaction, error) + Commit(ctx context.Context, in *Transaction, opts ...grpc.CallOption) (*spannerpb.CommitResponse, error) + Rollback(ctx context.Context, in *Transaction, opts ...grpc.CallOption) (*emptypb.Empty, error) + Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*spannerpb.CommitResponse, error) + BufferWrite(ctx context.Context, in *BufferWriteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + ConnectionStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ConnectionStreamRequest, ConnectionStreamResponse], error) +} + +type spannerLibClient struct { + cc grpc.ClientConnInterface +} + +func NewSpannerLibClient(cc grpc.ClientConnInterface) SpannerLibClient { + return &spannerLibClient{cc} +} + +func (c *spannerLibClient) CreatePool(ctx context.Context, in *CreatePoolRequest, opts ...grpc.CallOption) (*Pool, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Pool) + err := c.cc.Invoke(ctx, SpannerLib_CreatePool_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) ClosePool(ctx context.Context, in *Pool, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, SpannerLib_ClosePool_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) CreateConnection(ctx context.Context, in *CreateConnectionRequest, opts ...grpc.CallOption) (*Connection, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Connection) + err := c.cc.Invoke(ctx, SpannerLib_CreateConnection_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) CloseConnection(ctx context.Context, in *Connection, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, SpannerLib_CloseConnection_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) Execute(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (*Rows, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Rows) + err := c.cc.Invoke(ctx, SpannerLib_Execute_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) ExecuteStreaming(ctx context.Context, in *ExecuteRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[spannerpb.PartialResultSet], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &SpannerLib_ServiceDesc.Streams[0], SpannerLib_ExecuteStreaming_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[ExecuteRequest, spannerpb.PartialResultSet]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SpannerLib_ExecuteStreamingClient = grpc.ServerStreamingClient[spannerpb.PartialResultSet] + +func (c *spannerLibClient) ExecuteTransaction(ctx context.Context, in *ExecuteTransactionRequest, opts ...grpc.CallOption) (*Rows, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Rows) + err := c.cc.Invoke(ctx, SpannerLib_ExecuteTransaction_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) ExecuteBatchDml(ctx context.Context, in *ExecuteBatchDmlRequest, opts ...grpc.CallOption) (*spannerpb.ExecuteBatchDmlResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(spannerpb.ExecuteBatchDmlResponse) + err := c.cc.Invoke(ctx, SpannerLib_ExecuteBatchDml_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) Metadata(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*spannerpb.ResultSetMetadata, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(spannerpb.ResultSetMetadata) + err := c.cc.Invoke(ctx, SpannerLib_Metadata_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) Next(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*structpb.ListValue, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(structpb.ListValue) + err := c.cc.Invoke(ctx, SpannerLib_Next_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) ResultSetStats(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*spannerpb.ResultSetStats, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(spannerpb.ResultSetStats) + err := c.cc.Invoke(ctx, SpannerLib_ResultSetStats_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) CloseRows(ctx context.Context, in *Rows, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, SpannerLib_CloseRows_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) BeginTransaction(ctx context.Context, in *BeginTransactionRequest, opts ...grpc.CallOption) (*Transaction, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Transaction) + err := c.cc.Invoke(ctx, SpannerLib_BeginTransaction_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) Commit(ctx context.Context, in *Transaction, opts ...grpc.CallOption) (*spannerpb.CommitResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(spannerpb.CommitResponse) + err := c.cc.Invoke(ctx, SpannerLib_Commit_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) Rollback(ctx context.Context, in *Transaction, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, SpannerLib_Rollback_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*spannerpb.CommitResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(spannerpb.CommitResponse) + err := c.cc.Invoke(ctx, SpannerLib_Apply_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) BufferWrite(ctx context.Context, in *BufferWriteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, SpannerLib_BufferWrite_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *spannerLibClient) ConnectionStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ConnectionStreamRequest, ConnectionStreamResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &SpannerLib_ServiceDesc.Streams[1], SpannerLib_ConnectionStream_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[ConnectionStreamRequest, ConnectionStreamResponse]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SpannerLib_ConnectionStreamClient = grpc.BidiStreamingClient[ConnectionStreamRequest, ConnectionStreamResponse] + +// SpannerLibServer is the server API for SpannerLib service. +// All implementations must embed UnimplementedSpannerLibServer +// for forward compatibility. +type SpannerLibServer interface { + CreatePool(context.Context, *CreatePoolRequest) (*Pool, error) + ClosePool(context.Context, *Pool) (*emptypb.Empty, error) + CreateConnection(context.Context, *CreateConnectionRequest) (*Connection, error) + CloseConnection(context.Context, *Connection) (*emptypb.Empty, error) + Execute(context.Context, *ExecuteRequest) (*Rows, error) + ExecuteStreaming(*ExecuteRequest, grpc.ServerStreamingServer[spannerpb.PartialResultSet]) error + ExecuteTransaction(context.Context, *ExecuteTransactionRequest) (*Rows, error) + ExecuteBatchDml(context.Context, *ExecuteBatchDmlRequest) (*spannerpb.ExecuteBatchDmlResponse, error) + Metadata(context.Context, *Rows) (*spannerpb.ResultSetMetadata, error) + Next(context.Context, *Rows) (*structpb.ListValue, error) + ResultSetStats(context.Context, *Rows) (*spannerpb.ResultSetStats, error) + CloseRows(context.Context, *Rows) (*emptypb.Empty, error) + BeginTransaction(context.Context, *BeginTransactionRequest) (*Transaction, error) + Commit(context.Context, *Transaction) (*spannerpb.CommitResponse, error) + Rollback(context.Context, *Transaction) (*emptypb.Empty, error) + Apply(context.Context, *ApplyRequest) (*spannerpb.CommitResponse, error) + BufferWrite(context.Context, *BufferWriteRequest) (*emptypb.Empty, error) + ConnectionStream(grpc.BidiStreamingServer[ConnectionStreamRequest, ConnectionStreamResponse]) error + mustEmbedUnimplementedSpannerLibServer() +} + +// UnimplementedSpannerLibServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSpannerLibServer struct{} + +func (UnimplementedSpannerLibServer) CreatePool(context.Context, *CreatePoolRequest) (*Pool, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreatePool not implemented") +} +func (UnimplementedSpannerLibServer) ClosePool(context.Context, *Pool) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClosePool not implemented") +} +func (UnimplementedSpannerLibServer) CreateConnection(context.Context, *CreateConnectionRequest) (*Connection, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateConnection not implemented") +} +func (UnimplementedSpannerLibServer) CloseConnection(context.Context, *Connection) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CloseConnection not implemented") +} +func (UnimplementedSpannerLibServer) Execute(context.Context, *ExecuteRequest) (*Rows, error) { + return nil, status.Errorf(codes.Unimplemented, "method Execute not implemented") +} +func (UnimplementedSpannerLibServer) ExecuteStreaming(*ExecuteRequest, grpc.ServerStreamingServer[spannerpb.PartialResultSet]) error { + return status.Errorf(codes.Unimplemented, "method ExecuteStreaming not implemented") +} +func (UnimplementedSpannerLibServer) ExecuteTransaction(context.Context, *ExecuteTransactionRequest) (*Rows, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExecuteTransaction not implemented") +} +func (UnimplementedSpannerLibServer) ExecuteBatchDml(context.Context, *ExecuteBatchDmlRequest) (*spannerpb.ExecuteBatchDmlResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExecuteBatchDml not implemented") +} +func (UnimplementedSpannerLibServer) Metadata(context.Context, *Rows) (*spannerpb.ResultSetMetadata, error) { + return nil, status.Errorf(codes.Unimplemented, "method Metadata not implemented") +} +func (UnimplementedSpannerLibServer) Next(context.Context, *Rows) (*structpb.ListValue, error) { + return nil, status.Errorf(codes.Unimplemented, "method Next not implemented") +} +func (UnimplementedSpannerLibServer) ResultSetStats(context.Context, *Rows) (*spannerpb.ResultSetStats, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResultSetStats not implemented") +} +func (UnimplementedSpannerLibServer) CloseRows(context.Context, *Rows) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CloseRows not implemented") +} +func (UnimplementedSpannerLibServer) BeginTransaction(context.Context, *BeginTransactionRequest) (*Transaction, error) { + return nil, status.Errorf(codes.Unimplemented, "method BeginTransaction not implemented") +} +func (UnimplementedSpannerLibServer) Commit(context.Context, *Transaction) (*spannerpb.CommitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Commit not implemented") +} +func (UnimplementedSpannerLibServer) Rollback(context.Context, *Transaction) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Rollback not implemented") +} +func (UnimplementedSpannerLibServer) Apply(context.Context, *ApplyRequest) (*spannerpb.CommitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Apply not implemented") +} +func (UnimplementedSpannerLibServer) BufferWrite(context.Context, *BufferWriteRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method BufferWrite not implemented") +} +func (UnimplementedSpannerLibServer) ConnectionStream(grpc.BidiStreamingServer[ConnectionStreamRequest, ConnectionStreamResponse]) error { + return status.Errorf(codes.Unimplemented, "method ConnectionStream not implemented") +} +func (UnimplementedSpannerLibServer) mustEmbedUnimplementedSpannerLibServer() {} +func (UnimplementedSpannerLibServer) testEmbeddedByValue() {} + +// UnsafeSpannerLibServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SpannerLibServer will +// result in compilation errors. +type UnsafeSpannerLibServer interface { + mustEmbedUnimplementedSpannerLibServer() +} + +func RegisterSpannerLibServer(s grpc.ServiceRegistrar, srv SpannerLibServer) { + // If the following call pancis, it indicates UnimplementedSpannerLibServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&SpannerLib_ServiceDesc, srv) +} + +func _SpannerLib_CreatePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreatePoolRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).CreatePool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_CreatePool_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).CreatePool(ctx, req.(*CreatePoolRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_ClosePool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Pool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).ClosePool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_ClosePool_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).ClosePool(ctx, req.(*Pool)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_CreateConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateConnectionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).CreateConnection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_CreateConnection_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).CreateConnection(ctx, req.(*CreateConnectionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_CloseConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Connection) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).CloseConnection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_CloseConnection_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).CloseConnection(ctx, req.(*Connection)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_Execute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExecuteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).Execute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_Execute_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).Execute(ctx, req.(*ExecuteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_ExecuteStreaming_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ExecuteRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(SpannerLibServer).ExecuteStreaming(m, &grpc.GenericServerStream[ExecuteRequest, spannerpb.PartialResultSet]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SpannerLib_ExecuteStreamingServer = grpc.ServerStreamingServer[spannerpb.PartialResultSet] + +func _SpannerLib_ExecuteTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExecuteTransactionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).ExecuteTransaction(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_ExecuteTransaction_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).ExecuteTransaction(ctx, req.(*ExecuteTransactionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_ExecuteBatchDml_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExecuteBatchDmlRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).ExecuteBatchDml(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_ExecuteBatchDml_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).ExecuteBatchDml(ctx, req.(*ExecuteBatchDmlRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_Metadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Rows) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).Metadata(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_Metadata_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).Metadata(ctx, req.(*Rows)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_Next_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Rows) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).Next(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_Next_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).Next(ctx, req.(*Rows)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_ResultSetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Rows) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).ResultSetStats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_ResultSetStats_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).ResultSetStats(ctx, req.(*Rows)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_CloseRows_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Rows) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).CloseRows(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_CloseRows_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).CloseRows(ctx, req.(*Rows)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_BeginTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BeginTransactionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).BeginTransaction(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_BeginTransaction_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).BeginTransaction(ctx, req.(*BeginTransactionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_Commit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Transaction) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).Commit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_Commit_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).Commit(ctx, req.(*Transaction)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_Rollback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Transaction) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).Rollback(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_Rollback_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).Rollback(ctx, req.(*Transaction)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_Apply_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApplyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).Apply(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_Apply_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).Apply(ctx, req.(*ApplyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_BufferWrite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BufferWriteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SpannerLibServer).BufferWrite(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SpannerLib_BufferWrite_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SpannerLibServer).BufferWrite(ctx, req.(*BufferWriteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SpannerLib_ConnectionStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(SpannerLibServer).ConnectionStream(&grpc.GenericServerStream[ConnectionStreamRequest, ConnectionStreamResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type SpannerLib_ConnectionStreamServer = grpc.BidiStreamingServer[ConnectionStreamRequest, ConnectionStreamResponse] + +// SpannerLib_ServiceDesc is the grpc.ServiceDesc for SpannerLib service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var SpannerLib_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "google.spannerlib.v1.SpannerLib", + HandlerType: (*SpannerLibServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreatePool", + Handler: _SpannerLib_CreatePool_Handler, + }, + { + MethodName: "ClosePool", + Handler: _SpannerLib_ClosePool_Handler, + }, + { + MethodName: "CreateConnection", + Handler: _SpannerLib_CreateConnection_Handler, + }, + { + MethodName: "CloseConnection", + Handler: _SpannerLib_CloseConnection_Handler, + }, + { + MethodName: "Execute", + Handler: _SpannerLib_Execute_Handler, + }, + { + MethodName: "ExecuteTransaction", + Handler: _SpannerLib_ExecuteTransaction_Handler, + }, + { + MethodName: "ExecuteBatchDml", + Handler: _SpannerLib_ExecuteBatchDml_Handler, + }, + { + MethodName: "Metadata", + Handler: _SpannerLib_Metadata_Handler, + }, + { + MethodName: "Next", + Handler: _SpannerLib_Next_Handler, + }, + { + MethodName: "ResultSetStats", + Handler: _SpannerLib_ResultSetStats_Handler, + }, + { + MethodName: "CloseRows", + Handler: _SpannerLib_CloseRows_Handler, + }, + { + MethodName: "BeginTransaction", + Handler: _SpannerLib_BeginTransaction_Handler, + }, + { + MethodName: "Commit", + Handler: _SpannerLib_Commit_Handler, + }, + { + MethodName: "Rollback", + Handler: _SpannerLib_Rollback_Handler, + }, + { + MethodName: "Apply", + Handler: _SpannerLib_Apply_Handler, + }, + { + MethodName: "BufferWrite", + Handler: _SpannerLib_BufferWrite_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "ExecuteStreaming", + Handler: _SpannerLib_ExecuteStreaming_Handler, + ServerStreams: true, + }, + { + StreamName: "ConnectionStream", + Handler: _SpannerLib_ConnectionStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "google/spannerlib/v1/spannerlib.proto", +} diff --git a/spannerlib/grpc-server/README.md b/spannerlib/grpc-server/README.md new file mode 100644 index 00000000..6254293d --- /dev/null +++ b/spannerlib/grpc-server/README.md @@ -0,0 +1,6 @@ +# Shared Library gRPC Server (Internal) + +__This module can receive breaking changes without prior notice.__ + +This is an internal package that is used to expose the features in the database/sql driver +to drivers in other programming languages as a gRPC server. diff --git a/spannerlib/grpc-server/grpc_server.go b/spannerlib/grpc-server/grpc_server.go new file mode 100644 index 00000000..91e0cfce --- /dev/null +++ b/spannerlib/grpc-server/grpc_server.go @@ -0,0 +1,280 @@ +package main + +import ( + "context" + "io" + "log" + "net" + "os" + + "cloud.google.com/go/spanner/apiv1/spannerpb" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/structpb" + "spannerlib/api" + pb "spannerlib/google/spannerlib/v1" +) + +func main() { + if len(os.Args) < 2 { + log.Fatalf("Missing gRPC server address\n") + } + name := os.Args[1] + defer func() { _ = os.Remove(name) }() + lis, err := net.Listen("unix", name) + if err != nil { + log.Fatalf("failed to listen: %v\n", err) + } + var opts []grpc.ServerOption + grpcServer := grpc.NewServer(opts...) + + server := spannerLibServer{} + pb.RegisterSpannerLibServer(grpcServer, &server) + log.Printf("Starting gRPC server on %s\n", name) + err = grpcServer.Serve(lis) + if err != nil { + log.Fatalf("failed to serve: %v\n", err) + } else { + log.Printf("gRPC server started\n") + } +} + +var _ pb.SpannerLibServer = &spannerLibServer{} + +type spannerLibServer struct { + pb.UnimplementedSpannerLibServer +} + +func (s *spannerLibServer) CreatePool(ctx context.Context, request *pb.CreatePoolRequest) (*pb.Pool, error) { + id, err := api.CreatePool(request.Dsn) + if err != nil { + return nil, err + } + return &pb.Pool{Id: id}, nil +} + +func (s *spannerLibServer) ClosePool(ctx context.Context, pool *pb.Pool) (*emptypb.Empty, error) { + err := api.ClosePool(pool.Id) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, nil +} + +func (s *spannerLibServer) CreateConnection(ctx context.Context, request *pb.CreateConnectionRequest) (*pb.Connection, error) { + id, err := api.CreateConnection(request.Pool.Id) + if err != nil { + return nil, err + } + return &pb.Connection{Pool: request.Pool, Id: id}, nil +} + +func (s *spannerLibServer) CloseConnection(ctx context.Context, connection *pb.Connection) (*emptypb.Empty, error) { + err := api.CloseConnection(connection.Pool.Id, connection.Id) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, nil +} + +func (s *spannerLibServer) Execute(ctx context.Context, request *pb.ExecuteRequest) (*pb.Rows, error) { + id, err := api.Execute(request.Connection.Pool.Id, request.Connection.Id, request.ExecuteSqlRequest) + if err != nil { + return nil, err + } + return &pb.Rows{Connection: request.Connection, Id: id}, nil +} + +func (s *spannerLibServer) ExecuteStreaming(request *pb.ExecuteRequest, stream grpc.ServerStreamingServer[spannerpb.PartialResultSet]) error { + return s.executeStreaming(request, stream) +} + +type partialResultSetStream interface { + Send(*spannerpb.PartialResultSet) error +} + +func (s *spannerLibServer) executeStreaming(request *pb.ExecuteRequest, stream partialResultSetStream) error { + id, err := api.Execute(request.Connection.Pool.Id, request.Connection.Id, request.ExecuteSqlRequest) + defer func() { _ = api.CloseRows(request.Connection.Pool.Id, request.Connection.Id, id) }() + if err != nil { + return err + } + metadata, err := api.Metadata(request.Connection.Pool.Id, request.Connection.Id, id) + first := true + for { + if row, err := api.Next(request.Connection.Pool.Id, request.Connection.Id, id); err != nil { + return err + } else { + if row == nil { + stats, err := api.ResultSetStats(request.Connection.Pool.Id, request.Connection.Id, id) + if err != nil { + return err + } + res := &spannerpb.PartialResultSet{Stats: stats, Last: true} + if err := stream.Send(res); err != nil { + return err + } + break + } + res := &spannerpb.PartialResultSet{Values: row.Values} + if first { + res.Metadata = metadata + first = false + } + if err := stream.Send(res); err != nil { + return err + } + } + } + return nil +} + +func (s *spannerLibServer) sendData(data chan *spannerpb.PartialResultSet, stream partialResultSetStream) error { + for { + res, ok := <-data + if !ok { + return nil + } + if err := stream.Send(res); err != nil { + close(data) + return err + } + } +} + +//func (s *spannerLibServer) produceData(data chan *spannerpb.PartialResultSet, request *pb.ExecuteRequest) { +// id, err := api.Execute(request.Connection.Pool.Id, request.Connection.Id, request.ExecuteSqlRequest) +// defer func() { _ = api.CloseRows(request.Connection.Pool.Id, request.Connection.Id, id) }() +// if err != nil { +// return err +// } +// metadata, err := api.Metadata(request.Connection.Pool.Id, request.Connection.Id, id) +// first := true +// for { +// if row, err := api.Next(request.Connection.Pool.Id, request.Connection.Id, id); err != nil { +// return err +// } else { +// if row == nil { +// stats, err := api.ResultSetStats(request.Connection.Pool.Id, request.Connection.Id, id) +// if err != nil { +// return err +// } +// res := &spannerpb.PartialResultSet{Stats: stats, Last: true} +// if err := stream.Send(res); err != nil { +// return err +// } +// break +// } +// res := &spannerpb.PartialResultSet{Values: row.Values} +// if first { +// res.Metadata = metadata +// first = false +// } +// if err := stream.Send(res); err != nil { +// return err +// } +// } +// } +// return nil +//} + +func (s *spannerLibServer) ExecuteBatchDml(ctx context.Context, request *pb.ExecuteBatchDmlRequest) (*spannerpb.ExecuteBatchDmlResponse, error) { + resp, err := api.ExecuteBatch(request.Connection.Pool.Id, request.Connection.Id, request.ExecuteBatchDmlRequest) + if err != nil { + return nil, err + } + return resp, nil +} + +func (s *spannerLibServer) Metadata(ctx context.Context, rows *pb.Rows) (*spannerpb.ResultSetMetadata, error) { + metadata, err := api.Metadata(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id) + if err != nil { + return nil, err + } + return metadata, nil +} + +func (s *spannerLibServer) Next(ctx context.Context, rows *pb.Rows) (*structpb.ListValue, error) { + values, err := api.Next(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id) + if err != nil { + return nil, err + } + return values, nil +} + +func (s *spannerLibServer) ResultSetStats(ctx context.Context, rows *pb.Rows) (*spannerpb.ResultSetStats, error) { + stats, err := api.ResultSetStats(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id) + if err != nil { + return nil, err + } + return stats, nil +} + +func (s *spannerLibServer) CloseRows(ctx context.Context, rows *pb.Rows) (*emptypb.Empty, error) { + err := api.CloseRows(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, nil +} + +func (s *spannerLibServer) BeginTransaction(ctx context.Context, request *pb.BeginTransactionRequest) (*pb.Transaction, error) { + err := api.BeginTransaction(request.Connection.Pool.Id, request.Connection.Id, request.TransactionOptions) + if err != nil { + return nil, err + } + return &pb.Transaction{Connection: request.Connection, Id: 0}, nil +} + +func (s *spannerLibServer) Commit(ctx context.Context, request *pb.Transaction) (*spannerpb.CommitResponse, error) { + resp, err := api.Commit(request.Connection.Pool.Id, request.Connection.Id) + if err != nil { + return nil, err + } + return resp, nil +} + +func (s *spannerLibServer) Rollback(ctx context.Context, request *pb.Transaction) (*emptypb.Empty, error) { + err := api.Rollback(request.Connection.Pool.Id, request.Connection.Id) + if err != nil { + return nil, err + } + return &emptypb.Empty{}, nil +} + +func (s *spannerLibServer) Apply(ctx context.Context, request *pb.ApplyRequest) (*spannerpb.CommitResponse, error) { + resp, err := api.WriteMutations(request.Connection.Pool.Id, request.Connection.Id, request.Mutations) + if err != nil { + return nil, err + } + return resp, nil +} + +func (s *spannerLibServer) ConnectionStream(stream grpc.BidiStreamingServer[pb.ConnectionStreamRequest, pb.ConnectionStreamResponse]) error { + for { + req, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + if req.GetExecuteRequest() != nil { + if err := s.handleExecuteRequest(stream, req.GetExecuteRequest()); err != nil { + return err + } + } + } +} + +type connectionStreamResponseStream struct { + stream grpc.BidiStreamingServer[pb.ConnectionStreamRequest, pb.ConnectionStreamResponse] +} + +func (cs *connectionStreamResponseStream) Send(row *spannerpb.PartialResultSet) error { + return cs.stream.Send(&pb.ConnectionStreamResponse{Response: &pb.ConnectionStreamResponse_Row{Row: row}}) +} + +func (s *spannerLibServer) handleExecuteRequest(stream grpc.BidiStreamingServer[pb.ConnectionStreamRequest, pb.ConnectionStreamResponse], req *pb.ExecuteRequest) error { + return s.executeStreaming(req, &connectionStreamResponseStream{stream: stream}) +} diff --git a/spannerlib/lib/README.md b/spannerlib/lib/README.md new file mode 100644 index 00000000..0d730b15 --- /dev/null +++ b/spannerlib/lib/README.md @@ -0,0 +1,6 @@ +# Shared Library (Internal) + +__This module can receive breaking changes without prior notice.__ + +This is an internal package that is used to expose the features in the database/sql driver +to drivers in other programming languages as a C shared library. diff --git a/spannerlib/lib/benchmark_test.go b/spannerlib/lib/benchmark_test.go new file mode 100644 index 00000000..5a8faadd --- /dev/null +++ b/spannerlib/lib/benchmark_test.go @@ -0,0 +1,43 @@ +package lib + +import ( + "fmt" + "testing" + "time" + + "cloud.google.com/go/spanner/apiv1/spannerpb" + "google.golang.org/protobuf/proto" +) + +func TestExecute(t *testing.T) { + t.Skip("only for manual testing") + + pool := CreatePool("projects/appdev-soda-spanner-staging/instances/knut-test-probers/databases/prober") + conn := CreateConnection(pool.ObjectId) + request := spannerpb.ExecuteSqlRequest{Sql: "select * from all_types limit 10000"} + requestBytes, err := proto.Marshal(&request) + if err != nil { + t.Fatal(err) + } + + for range 50 { + rowCount := 0 + start := time.Now() + rows := Execute(pool.ObjectId, conn.ObjectId, requestBytes) + for { + row := Next(pool.ObjectId, conn.ObjectId, rows.ObjectId) + if row.Length() == 0 { + break + } + rowCount++ + } + CloseRows(pool.ObjectId, conn.ObjectId, rows.ObjectId) + end := time.Now() + fmt.Printf("Execution took %s\n", end.Sub(start)) + if g, w := rowCount, 10000; g != w { + t.Errorf("row count mismatch: got %d, want %d", g, w) + } + } + CloseConnection(pool.ObjectId, conn.ObjectId) + ClosePool(pool.ObjectId) +} diff --git a/spannerlib/lib/connection.go b/spannerlib/lib/connection.go new file mode 100644 index 00000000..8845bf7a --- /dev/null +++ b/spannerlib/lib/connection.go @@ -0,0 +1,72 @@ +package lib + +import "C" +import ( + "cloud.google.com/go/spanner/apiv1/spannerpb" + "google.golang.org/protobuf/proto" + "spannerlib/api" +) + +func CloseConnection(poolId, connId int64) *Message { + err := api.CloseConnection(poolId, connId) + if err != nil { + return errMessage(err) + } + return &Message{} +} + +func WriteMutations(poolId, connId int64, mutationBytes []byte) *Message { + mutations := spannerpb.BatchWriteRequest_MutationGroup{} + if err := proto.Unmarshal(mutationBytes, &mutations); err != nil { + return errMessage(err) + } + response, err := api.WriteMutations(poolId, connId, &mutations) + if err != nil { + return errMessage(err) + } + res, err := proto.Marshal(response) + if err != nil { + return errMessage(err) + } + return &Message{Res: res} +} + +func BeginTransaction(poolId, connId int64, txOptsBytes []byte) *Message { + txOpts := spannerpb.TransactionOptions{} + if err := proto.Unmarshal(txOptsBytes, &txOpts); err != nil { + return errMessage(err) + } + err := api.BeginTransaction(poolId, connId, &txOpts) + if err != nil { + return errMessage(err) + } + return &Message{} +} + +func Execute(poolId, connId int64, executeSqlRequestBytes []byte) *Message { + statement := spannerpb.ExecuteSqlRequest{} + if err := proto.Unmarshal(executeSqlRequestBytes, &statement); err != nil { + return errMessage(err) + } + id, err := api.Execute(poolId, connId, &statement) + if err != nil { + return errMessage(err) + } + return idMessage(id) +} + +func ExecuteBatch(poolId, connId int64, statementsBytes []byte) *Message { + statements := spannerpb.ExecuteBatchDmlRequest{} + if err := proto.Unmarshal(statementsBytes, &statements); err != nil { + return errMessage(err) + } + response, err := api.ExecuteBatch(poolId, connId, &statements) + if err != nil { + return errMessage(err) + } + res, err := proto.Marshal(response) + if err != nil { + return errMessage(err) + } + return &Message{Res: res} +} diff --git a/spannerlib/lib/message.go b/spannerlib/lib/message.go new file mode 100644 index 00000000..f45cf54f --- /dev/null +++ b/spannerlib/lib/message.go @@ -0,0 +1,40 @@ +package lib + +import ( + "unsafe" + + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +type Message struct { + Code int32 + ObjectId int64 + Res []byte +} + +func (m *Message) Length() int32 { + if m.Res == nil { + return 0 + } + return int32(len(m.Res)) +} + +func (m *Message) ResPointer() unsafe.Pointer { + if m.Res == nil { + return nil + } + return unsafe.Pointer(&(m.Res[0])) +} + +func idMessage(id int64) *Message { + return &Message{ObjectId: id} +} + +func errMessage(err error) *Message { + s := status.Convert(err) + p := s.Proto() + // Ignore any marshalling errors and just return an empty status in that case. + b, _ := proto.Marshal(p) + return &Message{Code: int32(s.Code()), Res: b} +} diff --git a/spannerlib/lib/mock_server_test.go b/spannerlib/lib/mock_server_test.go new file mode 100644 index 00000000..df4b778b --- /dev/null +++ b/spannerlib/lib/mock_server_test.go @@ -0,0 +1,569 @@ +package lib + +import ( + "context" + "database/sql" + "fmt" + "reflect" + "testing" + "time" + + "cloud.google.com/go/spanner" + "cloud.google.com/go/spanner/admin/database/apiv1/databasepb" + sppb "cloud.google.com/go/spanner/apiv1/spannerpb" + "github.com/google/uuid" + "github.com/googleapis/go-sql-spanner/testutil" + "google.golang.org/api/option" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestSimpleQuery(t *testing.T) { + t.Parallel() + + dsn, _, teardown := setupTestDBConnection(t) + defer teardown() + + pool := CreatePool(dsn) + conn := CreateConnection(pool.ObjectId) + statement := sppb.ExecuteSqlRequest{ + Sql: testutil.SelectFooFromBar, + } + statementBytes, err := proto.Marshal(&statement) + if err != nil { + t.Fatalf("failed to marshal statement: %v", err) + } + results := Execute(pool.ObjectId, conn.ObjectId, statementBytes) + metadataMsg := Metadata(pool.ObjectId, conn.ObjectId, results.ObjectId) + if metadataMsg.Code != 0 { + t.Fatalf("metadataMsg.Code: %v", metadataMsg.Code) + } + metadata := &sppb.ResultSetMetadata{} + if err := proto.Unmarshal(metadataMsg.Res, metadata); err != nil { + t.Fatalf("failed to unmarshal metadata: %v", err) + } + if g, w := len(metadata.RowType.Fields), 1; g != w { + t.Fatalf("field count mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := metadata.RowType.Fields[0].Name, "FOO"; g != w { + t.Fatalf("field name mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := metadata.RowType.Fields[0].Type.Code, sppb.TypeCode_INT64; g != w { + t.Fatalf("field type code mismatch\n Got: %v\nWant: %v", g, w) + } + for { + row := Next(pool.ObjectId, conn.ObjectId, results.ObjectId) + if row.Length() == 0 { + break + } + values := structpb.ListValue{} + if err := proto.Unmarshal(row.Res, &values); err != nil { + t.Fatalf("failed to unmarshal row: %v", err) + } + } + stats := ResultSetStats(pool.ObjectId, conn.ObjectId, results.ObjectId) + if stats.Code != 0 { + t.Fatalf("stats.Code: %v", stats.Code) + } + CloseRows(pool.ObjectId, conn.ObjectId, results.ObjectId) + CloseConnection(pool.ObjectId, conn.ObjectId) + ClosePool(pool.ObjectId) +} + +func generateLargeResultSet(numRows int) *sppb.ResultSet { + res := &sppb.ResultSet{ + Metadata: &sppb.ResultSetMetadata{ + RowType: &sppb.StructType{ + Fields: []*sppb.StructType_Field{ + {Type: &sppb.Type{Code: sppb.TypeCode_STRING}, Name: "col1"}, + {Type: &sppb.Type{Code: sppb.TypeCode_STRING}, Name: "col2"}, + {Type: &sppb.Type{Code: sppb.TypeCode_STRING}, Name: "col3"}, + {Type: &sppb.Type{Code: sppb.TypeCode_STRING}, Name: "col4"}, + {Type: &sppb.Type{Code: sppb.TypeCode_STRING}, Name: "col5"}, + }, + }, + }, + } + rows := make([]*structpb.ListValue, 0, numRows) + for i := 0; i < numRows; i++ { + values := make([]*structpb.Value, 0, 5) + for j := 0; j < 5; j++ { + values = append(values, &structpb.Value{ + Kind: &structpb.Value_StringValue{StringValue: uuid.New().String()}, + }) + } + rows = append(rows, &structpb.ListValue{ + Values: values, + }) + } + res.Rows = rows + return res +} + +func TestLargeQuery(t *testing.T) { + t.Skip("only for manual testing") + t.Parallel() + + dsn, server, teardown := setupTestDBConnection(t) + defer teardown() + + _ = server.TestSpanner.PutStatementResult("select * from all_types", &testutil.StatementResult{ + Type: testutil.StatementResultResultSet, + ResultSet: generateLargeResultSet(1000000), + }) + + pool := CreatePool(dsn) + conn := CreateConnection(pool.ObjectId) + statement := sppb.ExecuteSqlRequest{ + Sql: "select * from all_types", + } + statementBytes, err := proto.Marshal(&statement) + if err != nil { + t.Fatalf("failed to marshal statement: %v", err) + } + + startTime := time.Now() + results := Execute(pool.ObjectId, conn.ObjectId, statementBytes) + metadata := Metadata(pool.ObjectId, conn.ObjectId, results.ObjectId) + if metadata.Code != 0 { + t.Fatalf("metadata.Code: %v", metadata.Code) + } + for { + row := Next(pool.ObjectId, conn.ObjectId, results.ObjectId) + if row.Length() == 0 { + break + } + values := structpb.ListValue{} + if err := proto.Unmarshal(row.Res, &values); err != nil { + t.Fatalf("failed to unmarshal row: %v", err) + } + } + stats := ResultSetStats(pool.ObjectId, conn.ObjectId, results.ObjectId) + if stats.Code != 0 { + t.Fatalf("stats.Code: %v", stats.Code) + } + CloseRows(pool.ObjectId, conn.ObjectId, results.ObjectId) + endTime := time.Now() + + fmt.Printf("Query took %v\n", endTime.Sub(startTime)) + + CloseConnection(pool.ObjectId, conn.ObjectId) + ClosePool(pool.ObjectId) +} + +func TestQueryWithTimestampBound(t *testing.T) { + t.Parallel() + + dsn, server, teardown := setupTestDBConnection(t) + defer teardown() + + pool := CreatePool(dsn) + conn := CreateConnection(pool.ObjectId) + statement := sppb.ExecuteSqlRequest{ + Sql: testutil.SelectFooFromBar, + Transaction: &sppb.TransactionSelector{ + Selector: &sppb.TransactionSelector_SingleUse{ + SingleUse: &sppb.TransactionOptions{ + Mode: &sppb.TransactionOptions_ReadOnly_{ + ReadOnly: &sppb.TransactionOptions_ReadOnly{ + TimestampBound: &sppb.TransactionOptions_ReadOnly_MaxStaleness{ + MaxStaleness: &durationpb.Duration{Seconds: 10}, + }, + }, + }, + }, + }, + }, + } + statementBytes, err := proto.Marshal(&statement) + if err != nil { + t.Fatalf("failed to marshal statement: %v", err) + } + results := Execute(pool.ObjectId, conn.ObjectId, statementBytes) + metadata := Metadata(pool.ObjectId, conn.ObjectId, results.ObjectId) + if metadata.Code != 0 { + t.Fatalf("metadata.Code: %v", metadata.Code) + } + for { + row := Next(pool.ObjectId, conn.ObjectId, results.ObjectId) + if row.Length() == 0 { + break + } + values := structpb.ListValue{} + if err := proto.Unmarshal(row.Res, &values); err != nil { + t.Fatalf("failed to unmarshal row: %v", err) + } + } + stats := ResultSetStats(pool.ObjectId, conn.ObjectId, results.ObjectId) + if stats.Code != 0 { + t.Fatalf("stats.Code: %v", stats.Code) + } + CloseRows(pool.ObjectId, conn.ObjectId, results.ObjectId) + CloseConnection(pool.ObjectId, conn.ObjectId) + ClosePool(pool.ObjectId) + + requests := server.TestSpanner.DrainRequestsFromServer() + sqlRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.ExecuteSqlRequest{})) + if g, w := len(sqlRequests), 1; g != w { + t.Fatalf("sql requests count mismatch\nGot: %v\nWant: %v", g, w) + } + req := sqlRequests[0].(*sppb.ExecuteSqlRequest) + if req.Transaction == nil { + t.Fatalf("missing transaction for ExecuteSqlRequest") + } + if req.Transaction.GetSingleUse() == nil { + t.Fatalf("missing single use selector for ExecuteSqlRequest") + } + if req.Transaction.GetSingleUse().GetReadOnly() == nil { + t.Fatalf("missing read-only option for ExecuteSqlRequest") + } + if req.Transaction.GetSingleUse().GetReadOnly().GetMaxStaleness() == nil { + t.Fatalf("missing max staleness timestampbound for ExecuteSqlRequest") + } + if g, w := req.Transaction.GetSingleUse().GetReadOnly().GetMaxStaleness().GetSeconds(), int64(10); g != w { + t.Fatalf("max staleness seconds mismatch\n Got: %v\nWant: %v", g, w) + } +} + +func TestDisableInternalRetries(t *testing.T) { + t.Parallel() + + dsn, server, teardown := setupTestDBConnection(t) + defer teardown() + + pool := CreatePool(dsn) + //defer ClosePool(pool.ObjectId) + conn := CreateConnection(pool.ObjectId) + //defer CloseConnection(pool.ObjectId, conn.ObjectId) + + txOpts := &sppb.TransactionOptions{} + txOptsBytes, _ := proto.Marshal(txOpts) + tx := BeginTransaction(pool.ObjectId, conn.ObjectId, txOptsBytes) + if tx.Code != 0 { + t.Fatalf("tx.Code: %v", tx.Code) + } + + statement := sppb.ExecuteSqlRequest{ + Sql: "set retry_aborts_internally = false", + } + statementBytes, _ := proto.Marshal(&statement) + results := Execute(pool.ObjectId, conn.ObjectId, statementBytes) + if results.Code != 0 { + t.Fatalf("failed to set retry_aborts_internally: %v", results.Code) + } + CloseRows(pool.ObjectId, conn.ObjectId, results.ObjectId) + + statement = sppb.ExecuteSqlRequest{ + Sql: testutil.UpdateBarSetFoo, + } + statementBytes, _ = proto.Marshal(&statement) + results = Execute(pool.ObjectId, conn.ObjectId, statementBytes) + if results.Code != 0 { + t.Fatalf("failed to execute update: %v", results.Code) + } + CloseRows(pool.ObjectId, conn.ObjectId, results.ObjectId) + + server.TestSpanner.PutExecutionTime(testutil.MethodCommitTransaction, testutil.SimulatedExecutionTime{ + Errors: []error{status.Error(codes.Aborted, "Aborted")}, + }) + results = Commit(pool.ObjectId, conn.ObjectId) + if g, w := codes.Code(results.Code), codes.Aborted; g != w { + t.Fatalf("commit status mismatch\n Got: %v\nWant: %v", g, w) + } + CloseRows(pool.ObjectId, conn.ObjectId, results.ObjectId) + CloseConnection(pool.ObjectId, conn.ObjectId) + ClosePool(pool.ObjectId) +} + +func TestApply(t *testing.T) { + t.Parallel() + + dsn, server, teardown := setupTestDBConnection(t) + defer teardown() + + pool := CreatePool(dsn) + defer ClosePool(pool.ObjectId) + conn := CreateConnection(pool.ObjectId) + defer CloseConnection(pool.ObjectId, conn.ObjectId) + + mutations := sppb.BatchWriteRequest_MutationGroup{ + Mutations: []*sppb.Mutation{ + {Operation: &sppb.Mutation_Insert{Insert: &sppb.Mutation_Write{ + Table: "foo", + Columns: []string{"id", "value"}, + Values: []*structpb.ListValue{ + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "1"}}, + {Kind: &structpb.Value_StringValue{StringValue: "One"}}, + }}, + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "2"}}, + {Kind: &structpb.Value_StringValue{StringValue: "Two"}}, + }}, + }, + }}}, + }, + } + mutationsBytes, _ := proto.Marshal(&mutations) + response := WriteMutations(pool.ObjectId, conn.ObjectId, mutationsBytes) + if response.Code != 0 { + t.Fatalf("failed to apply mutations: %v", response.Code) + } + commitResponse := sppb.CommitResponse{} + _ = proto.Unmarshal(response.Res, &commitResponse) + if commitResponse.CommitTimestamp == nil { + t.Fatal("commit timestamp missing") + } + + requests := server.TestSpanner.DrainRequestsFromServer() + beginRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.BeginTransactionRequest{})) + if g, w := len(beginRequests), 1; g != w { + t.Fatalf("begin requests count mismatch\nGot: %v\nWant: %v", g, w) + } + req := beginRequests[0].(*sppb.BeginTransactionRequest) + if req.Options == nil { + t.Fatalf("missing tx opts") + } + if req.Options.GetReadWrite() == nil { + t.Fatalf("missing tx read write") + } + commitRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.CommitRequest{})) + if g, w := len(commitRequests), 1; g != w { + t.Fatalf("commit requests count mismatch\nGot: %v\nWant: %v", g, w) + } + commitReq := commitRequests[0].(*sppb.CommitRequest) + if g, w := len(commitReq.Mutations), 1; g != w { + t.Fatalf("mutation count mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := len(commitReq.Mutations[0].GetInsert().Values), 2; g != w { + t.Fatalf("mutation values count mismatch\n Got: %v\nWant: %v", g, w) + } +} + +func TestBufferWrite(t *testing.T) { + t.Parallel() + + dsn, server, teardown := setupTestDBConnection(t) + defer teardown() + + pool := CreatePool(dsn) + defer ClosePool(pool.ObjectId) + conn := CreateConnection(pool.ObjectId) + defer CloseConnection(pool.ObjectId, conn.ObjectId) + + txOpts := &sppb.TransactionOptions{} + txOptsBytes, _ := proto.Marshal(txOpts) + _ = BeginTransaction(pool.ObjectId, conn.ObjectId, txOptsBytes) + + mutations := sppb.BatchWriteRequest_MutationGroup{ + Mutations: []*sppb.Mutation{ + {Operation: &sppb.Mutation_Insert{Insert: &sppb.Mutation_Write{ + Table: "foo", + Columns: []string{"id", "value"}, + Values: []*structpb.ListValue{ + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "1"}}, + {Kind: &structpb.Value_StringValue{StringValue: "One"}}, + }}, + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "2"}}, + {Kind: &structpb.Value_StringValue{StringValue: "Two"}}, + }}, + }, + }}}, + }, + } + mutationsBytes, _ := proto.Marshal(&mutations) + response := WriteMutations(pool.ObjectId, conn.ObjectId, mutationsBytes) + if response.Code != 0 { + t.Fatalf("failed to apply mutations: %v", response.Code) + } + if response.Length() > 0 { + t.Fatal("response length mismatch") + } + + requests := server.TestSpanner.DrainRequestsFromServer() + // There should not be any BeginTransaction requests yet, as we use inlined-begin. + beginRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.BeginTransactionRequest{})) + if g, w := len(beginRequests), 0; g != w { + t.Fatalf("begin requests count mismatch\nGot: %v\nWant: %v", g, w) + } + // There should not be any commit requests yet. + commitRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.CommitRequest{})) + if g, w := len(commitRequests), 0; g != w { + t.Fatalf("commit requests count mismatch\nGot: %v\nWant: %v", g, w) + } + + // Commit the transaction with the mutation. + res := Commit(pool.ObjectId, conn.ObjectId) + if res.Code != 0 { + t.Fatalf("failed to commit: %v", res.Code) + } + + // Verify that we have a commit request on the server. + requests = server.TestSpanner.DrainRequestsFromServer() + beginRequests = testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.BeginTransactionRequest{})) + if g, w := len(beginRequests), 1; g != w { + t.Fatalf("begin requests count mismatch\nGot: %v\nWant: %v", g, w) + } + req := beginRequests[0].(*sppb.BeginTransactionRequest) + if req.Options == nil { + t.Fatalf("missing tx opts") + } + if req.Options.GetReadWrite() == nil { + t.Fatalf("missing tx read write") + } + commitRequests = testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.CommitRequest{})) + if g, w := len(commitRequests), 1; g != w { + t.Fatalf("commit requests count mismatch\nGot: %v\nWant: %v", g, w) + } + commitReq := commitRequests[0].(*sppb.CommitRequest) + if g, w := len(commitReq.Mutations), 1; g != w { + t.Fatalf("mutation count mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := len(commitReq.Mutations[0].GetInsert().Values), 2; g != w { + t.Fatalf("mutation values count mismatch\n Got: %v\nWant: %v", g, w) + } + +} + +func TestBufferWrite_RetryAborted(t *testing.T) { + t.Parallel() + + dsn, server, teardown := setupTestDBConnection(t) + defer teardown() + + pool := CreatePool(dsn) + defer ClosePool(pool.ObjectId) + conn := CreateConnection(pool.ObjectId) + defer CloseConnection(pool.ObjectId, conn.ObjectId) + + txOpts := &sppb.TransactionOptions{} + txOptsBytes, _ := proto.Marshal(txOpts) + _ = BeginTransaction(pool.ObjectId, conn.ObjectId, txOptsBytes) + + mutations := sppb.BatchWriteRequest_MutationGroup{ + Mutations: []*sppb.Mutation{ + {Operation: &sppb.Mutation_Insert{Insert: &sppb.Mutation_Write{ + Table: "foo", + Columns: []string{"id", "value"}, + Values: []*structpb.ListValue{ + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "1"}}, + {Kind: &structpb.Value_StringValue{StringValue: "One"}}, + }}, + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "2"}}, + {Kind: &structpb.Value_StringValue{StringValue: "Two"}}, + }}, + }, + }}}, + }, + } + mutationsBytes, _ := proto.Marshal(&mutations) + response := WriteMutations(pool.ObjectId, conn.ObjectId, mutationsBytes) + if response.Code != 0 { + t.Fatalf("failed to apply mutations: %v", response.Code) + } + if response.Length() > 0 { + t.Fatal("response length mismatch") + } + + requests := server.TestSpanner.DrainRequestsFromServer() + // There should not be any commit requests yet. + commitRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.CommitRequest{})) + if g, w := len(commitRequests), 0; g != w { + t.Fatalf("commit requests count mismatch\nGot: %v\nWant: %v", g, w) + } + + // Instruct the mock server to abort the transaction. + server.TestSpanner.PutExecutionTime(testutil.MethodCommitTransaction, testutil.SimulatedExecutionTime{ + Errors: []error{status.Error(codes.Aborted, "Aborted")}, + }) + + // Commit the transaction with the mutation. + res := Commit(pool.ObjectId, conn.ObjectId) + if res.Code != 0 { + t.Fatalf("failed to commit: %v", res.Code) + } + + // Verify that we have both begin and commit requests on the server. + requests = server.TestSpanner.DrainRequestsFromServer() + beginRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.BeginTransactionRequest{})) + if g, w := len(beginRequests), 2; g != w { + t.Fatalf("begin requests count mismatch\nGot: %v\nWant: %v", g, w) + } + for _, beginReq := range beginRequests { + req := beginReq.(*sppb.BeginTransactionRequest) + if req.Options == nil { + t.Fatalf("missing tx opts") + } + if req.Options.GetReadWrite() == nil { + t.Fatalf("missing tx read write") + } + } + commitRequests = testutil.RequestsOfType(requests, reflect.TypeOf(&sppb.CommitRequest{})) + if g, w := len(commitRequests), 2; g != w { + t.Fatalf("commit requests count mismatch\nGot: %v\nWant: %v", g, w) + } + for _, req := range commitRequests { + commitReq := req.(*sppb.CommitRequest) + if g, w := len(commitReq.Mutations), 1; g != w { + t.Fatalf("mutation count mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := len(commitReq.Mutations[0].GetInsert().Values), 2; g != w { + t.Fatalf("mutation values count mismatch\n Got: %v\nWant: %v", g, w) + } + } +} + +func setupTestDBConnection(t *testing.T) (dsn string, server *testutil.MockedSpannerInMemTestServer, teardown func()) { + return setupTestDBConnectionWithParams(t, "") +} + +func setupTestDBConnectionWithDialect(t *testing.T, dialect databasepb.DatabaseDialect) (dsn string, server *testutil.MockedSpannerInMemTestServer, teardown func()) { + return setupTestDBConnectionWithParamsAndDialect(t, "", dialect) +} + +func setupTestDBConnectionWithParams(t *testing.T, params string) (dsn string, server *testutil.MockedSpannerInMemTestServer, teardown func()) { + return setupTestDBConnectionWithParamsAndDialect(t, params, databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL) +} + +func setupMockedTestServerWithDialect(t *testing.T, dialect databasepb.DatabaseDialect) (server *testutil.MockedSpannerInMemTestServer, client *spanner.Client, teardown func()) { + return setupMockedTestServerWithConfigAndClientOptionsAndDialect(t, spanner.ClientConfig{}, []option.ClientOption{}, dialect) +} + +func setupTestDBConnectionWithParamsAndDialect(t *testing.T, params string, dialect databasepb.DatabaseDialect) (dsn string, server *testutil.MockedSpannerInMemTestServer, teardown func()) { + server, _, serverTeardown := setupMockedTestServerWithDialect(t, dialect) + dsn = fmt.Sprintf("%s/projects/p/instances/i/databases/d?useplaintext=true;%s", server.Address, params) + db, err := sql.Open("spanner", dsn) + if err != nil { + serverTeardown() + t.Fatal(err) + } + return dsn, server, func() { + _ = db.Close() + serverTeardown() + } +} + +func setupMockedTestServerWithConfigAndClientOptionsAndDialect(t *testing.T, config spanner.ClientConfig, clientOptions []option.ClientOption, dialect databasepb.DatabaseDialect) (server *testutil.MockedSpannerInMemTestServer, client *spanner.Client, teardown func()) { + server, opts, serverTeardown := testutil.NewMockedSpannerInMemTestServer(t) + server.SetupSelectDialectResult(dialect) + + opts = append(opts, clientOptions...) + ctx := context.Background() + formattedDatabase := fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]") + config.DisableNativeMetrics = true + client, err := spanner.NewClientWithConfig(ctx, formattedDatabase, config, opts...) + if err != nil { + t.Fatal(err) + } + return server, client, func() { + client.Close() + serverTeardown() + } +} diff --git a/spannerlib/lib/pool.go b/spannerlib/lib/pool.go new file mode 100644 index 00000000..e9da027e --- /dev/null +++ b/spannerlib/lib/pool.go @@ -0,0 +1,34 @@ +package lib + +import "C" +import ( + "fmt" + + "spannerlib/api" +) + +func CreatePool(dsn string) *Message { + // Copy the DSN into Go managed memory, as the DSN is stored in the connector config that is created. + dsn = fmt.Sprintf("%s", dsn) + + id, err := api.CreatePool(dsn) + if err != nil { + return errMessage(err) + } + return idMessage(id) +} + +func ClosePool(id int64) *Message { + if err := api.ClosePool(id); err != nil { + return errMessage(err) + } + return &Message{} +} + +func CreateConnection(poolId int64) *Message { + conn, err := api.CreateConnection(poolId) + if err != nil { + return errMessage(err) + } + return idMessage(conn) +} diff --git a/spannerlib/lib/rows.go b/spannerlib/lib/rows.go new file mode 100644 index 00000000..9cef8655 --- /dev/null +++ b/spannerlib/lib/rows.go @@ -0,0 +1,46 @@ +package lib + +import ( + "google.golang.org/protobuf/proto" + "spannerlib/api" +) + +func Metadata(poolId, connId, rowsId int64) *Message { + metadata, err := api.Metadata(poolId, connId, rowsId) + if err != nil { + return errMessage(err) + } + metadataBytes, err := proto.Marshal(metadata) + if err != nil { + return errMessage(err) + } + return &Message{Res: metadataBytes} +} + +func ResultSetStats(poolId, connId, rowsId int64) *Message { + stats, err := api.ResultSetStats(poolId, connId, rowsId) + if err != nil { + return errMessage(err) + } + statsBytes, err := proto.Marshal(stats) + if err != nil { + return errMessage(err) + } + return &Message{Res: statsBytes} +} + +func Next(poolId, connId, rowsId int64) *Message { + valuesBytes, err := api.NextEncoded(poolId, connId, rowsId) + if err != nil { + return errMessage(err) + } + return &Message{Res: valuesBytes} +} + +func CloseRows(poolId, connId, rowsId int64) *Message { + err := api.CloseRows(poolId, connId, rowsId) + if err != nil { + return errMessage(err) + } + return &Message{} +} diff --git a/spannerlib/lib/transaction.go b/spannerlib/lib/transaction.go new file mode 100644 index 00000000..20e74a67 --- /dev/null +++ b/spannerlib/lib/transaction.go @@ -0,0 +1,45 @@ +package lib + +import ( + "cloud.google.com/go/spanner/apiv1/spannerpb" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "spannerlib/api" +) + +func ExecuteTransaction(poolId, connId, txId int64, statementBytes []byte) *Message { + statement := spannerpb.ExecuteSqlRequest{} + if err := proto.Unmarshal(statementBytes, &statement); err != nil { + return errMessage(err) + } + //id, err := api.ExecuteTransaction(poolId, connId, txId, &statement) + err := status.Error(codes.Unimplemented, "not yet implemented") + if err != nil { + return errMessage(err) + } + return idMessage(0) +} + +func Commit(poolId, connId int64) *Message { + response, err := api.Commit(poolId, connId) + if err != nil { + return errMessage(err) + } + if response == nil { + return &Message{} + } + res, err := proto.Marshal(response) + if err != nil { + return errMessage(err) + } + return &Message{Res: res} +} + +func Rollback(poolId, connId int64) *Message { + err := api.Rollback(poolId, connId) + if err != nil { + return errMessage(err) + } + return &Message{} +} diff --git a/spannerlib/shared/build.sh b/spannerlib/shared/build.sh new file mode 100755 index 00000000..a8841db0 --- /dev/null +++ b/spannerlib/shared/build.sh @@ -0,0 +1 @@ +go build -o spannerlib.so -buildmode=c-shared shared_lib.go diff --git a/spannerlib/shared/shared_lib.go b/spannerlib/shared/shared_lib.go new file mode 100644 index 00000000..831087ac --- /dev/null +++ b/spannerlib/shared/shared_lib.go @@ -0,0 +1,208 @@ +package main + +import "C" +import ( + "runtime" + "sync" + "sync/atomic" + "unsafe" + + "spannerlib/lib" +) + +// An (empty) main function is required for C libraries. +func main() {} + +var ( + pinners = sync.Map{} + pinnerIdx atomic.Int64 +) + +// Release releases (unpins) a previously pinned message. Pinners are created and +// returned for each function call in this library, and it is the responsibility of +// the caller to call Release when it is done with the data that was returned. +// Note that 'done' can also mean 'the data has been copied into memory that is +// managed by the caller'. +// +//export Release +func Release(pinnerId int64) int32 { + if pinnerId <= 0 { + return 0 + } + val, ok := pinners.LoadAndDelete(pinnerId) + if !ok { + return 1 + } + pinner := val.(*runtime.Pinner) + pinner.Unpin() + return 0 +} + +// pin pins the memory location pointed to by the result of the given message. +// This prevents the Go runtime from moving or garbage collecting this memory. +// The returned pinner ID must be used to call Release when the caller is done +// with the message. +func pin(msg *lib.Message) (int64, int32, int64, int32, unsafe.Pointer) { + if msg.Length() == 0 { + return 0, msg.Code, msg.ObjectId, 0, nil + } + pinner := &runtime.Pinner{} + pinner.Pin(&(msg.Res[0])) + idx := pinnerIdx.Add(1) + pinners.Store(idx, pinner) + return idx, msg.Code, msg.ObjectId, msg.Length(), msg.ResPointer() +} + +// CreatePool creates a pool of database connections. A Pool is equivalent to a *sql.DB. +// All connections that are created from a pool share the same underlying Spanner client. +// +//export CreatePool +func CreatePool(dsn string) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.CreatePool(dsn) + return pin(msg) +} + +// ClosePool closes a previously opened Pool. All connections in the pool are also closed. +// +//export ClosePool +func ClosePool(id int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.ClosePool(id) + return pin(msg) +} + +// CreateConnection creates or borrows a connection from a previously created pool. +// Note that as Spanner does not really use a 'connection-based' API, creating a +// connection is a relatively cheap operation. It does not physically create a new +// gRPC channel or any other physical connection to Spanner, and it also does not +// create a server-side session. Instead, all session state is stored in the client. +// +//export CreateConnection +func CreateConnection(poolId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.CreateConnection(poolId) + return pin(msg) +} + +// CloseConnection closes a previously opened connection and releases all resources +// associated with the connection. +// +//export CloseConnection +func CloseConnection(poolId, connId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.CloseConnection(poolId, connId) + return pin(msg) +} + +// WriteMutations writes an array of mutations to Spanner. The mutations are buffered in +// the current read/write transaction if the connection currently has a read/write transaction. +// The mutations are applied to the database in a new read/write transaction that is automatically +// committed if the connection currently does not have a transaction. +// +// The function returns an error if the connection is currently in a read-only transaction. +// +// The mutationsBytes must be an encoded BatchWriteRequest_MutationGroup protobuf object. +// +//export WriteMutations +func WriteMutations(poolId, connectionId int64, mutationsBytes []byte) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.WriteMutations(poolId, connectionId, mutationsBytes) + return pin(msg) +} + +// Execute executes a SQL statement on the given connection. +// The return type is an identifier for a Rows object. This identifier can be used to +// call the functions Metadata and Next to get respectively the metadata of the result +// and the next row of results. +// +//export Execute +func Execute(poolId, connectionId int64, statement []byte) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.Execute(poolId, connectionId, statement) + return pin(msg) +} + +// ExecuteTransaction executes a statement using a specific transaction. +// +//export ExecuteTransaction +func ExecuteTransaction(poolId, connectionId, txId int64, statement []byte) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.ExecuteTransaction(poolId, connectionId, txId, statement) + return pin(msg) +} + +// ExecuteBatch executes a batch of statements on the given connection. The statements must all be either DML or DDL +// statements. Mixing DML and DDL in a batch is not supported. Executing queries in a batch is also not supported. +// +//export ExecuteBatch +func ExecuteBatch(poolId, connectionId int64, statements []byte) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.ExecuteBatch(poolId, connectionId, statements) + return pin(msg) +} + +// Metadata returns the metadata of a Rows object. +// +//export Metadata +func Metadata(poolId, connId, rowsId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.Metadata(poolId, connId, rowsId) + return pin(msg) +} + +// ResultSetStats returns the statistics for a statement that has been executed. This includes +// the number of rows affected in case of a DML statement. +// Statistics are only available once all rows have been consumed. +// +//export ResultSetStats +func ResultSetStats(poolId, connId, rowsId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.ResultSetStats(poolId, connId, rowsId) + return pin(msg) +} + +// Next returns the next row in a Rows object. The returned message contains a protobuf +// ListValue that contains all the columns of the row. The message is empty if there are +// no more rows in the Rows object. +// +//export Next +func Next(poolId, connId, rowsId int64, numRows int32, encodeRowOption int32) (int64, int32, int64, int32, unsafe.Pointer) { + // TODO: Implement support for: + // 1. Fetching more than one row at a time. + // 2. Specifying the return type (e.g. proto, struct, ...) + msg := lib.Next(poolId, connId, rowsId) + return pin(msg) +} + +// CloseRows closes and cleans up all memory held by a Rows object. This must be called +// when the application is done with the Rows object. +// +//export CloseRows +func CloseRows(poolId, connId, rowsId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.CloseRows(poolId, connId, rowsId) + return pin(msg) +} + +// BeginTransaction begins a new transaction on the given connection. +// The txOpts byte slice contains a serialized protobuf TransactionOptions object. +// +//export BeginTransaction +func BeginTransaction(poolId, connectionId int64, txOpts []byte) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.BeginTransaction(poolId, connectionId, txOpts) + return pin(msg) +} + +// Commit commits the current transaction on a connection. All transactions must be +// either committed or rolled back, including read-only transactions. This to ensure +// that all resources that are held by a transaction are cleaned up. +// +//export Commit +func Commit(poolId, connectionId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.Commit(poolId, connectionId) + return pin(msg) +} + +// Rollback rolls back a previously started transaction. All transactions must be either +// committed or rolled back, including read-only transactions. This to ensure that +// all resources that are held by a transaction are cleaned up. +// +// Spanner does not require read-only transactions to be committed or rolled back, but +// this library requires that all transactions are committed or rolled back to clean up +// all resources. Commit and Rollback are semantically the same for read-only transactions. +// +//export Rollback +func Rollback(poolId, connectionId int64) (int64, int32, int64, int32, unsafe.Pointer) { + msg := lib.Rollback(poolId, connectionId) + return pin(msg) +} diff --git a/spannerlib/shared/shared_lib_test.go b/spannerlib/shared/shared_lib_test.go new file mode 100644 index 00000000..357a03dc --- /dev/null +++ b/spannerlib/shared/shared_lib_test.go @@ -0,0 +1,250 @@ +package main + +import ( + "fmt" + "reflect" + "testing" + "unsafe" + + "cloud.google.com/go/spanner/admin/database/apiv1/databasepb" + "cloud.google.com/go/spanner/apiv1/spannerpb" + "github.com/google/uuid" + "github.com/googleapis/go-sql-spanner/testutil" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestCreateAndClosePool(t *testing.T) { + server, teardown := setupMockServer(t, databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL) + defer teardown() + + project := "my-project" + instance := "my-instance" + database := "my-database" + dsn := fmt.Sprintf("//%s/projects/%s/instances/%s/databases/%s;usePlainText=true", server.Address, project, instance, database) + pinner, code, poolId, length, ptr := CreatePool(dsn) + verifyEmptyIdMessage(t, "CreatePool", pinner, code, poolId, length, ptr) + + pinner, code, _, length, ptr = ClosePool(poolId) + verifyEmptyMessage(t, "ClosePool", pinner, code, length, ptr) +} + +func TestCreateAndCloseConnection(t *testing.T) { + server, teardown := setupMockServer(t, databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL) + defer teardown() + + project := "my-project" + instance := "my-instance" + database := "my-database" + dsn := fmt.Sprintf("//%s/projects/%s/instances/%s/databases/%s;usePlainText=true", server.Address, project, instance, database) + _, code, poolId, _, _ := CreatePool(dsn) + if g, w := code, int32(codes.OK); g != w { + t.Fatalf("CreatePool returned non-OK code\n Got: %v\nWant: %d", g, w) + } + defer ClosePool(poolId) + + pinner, code, connId, length, ptr := CreateConnection(poolId) + verifyEmptyIdMessage(t, "CreateConnection", pinner, code, connId, length, ptr) + + pinner, code, _, length, ptr = CloseConnection(poolId, connId) + verifyEmptyMessage(t, "CloseConnection", pinner, code, length, ptr) +} + +func TestApply(t *testing.T) { + poolId, connId, server, teardown := setupTestConnection(t, databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL) + defer teardown() + + mutations := &spannerpb.BatchWriteRequest_MutationGroup{ + Mutations: []*spannerpb.Mutation{ + {Operation: &spannerpb.Mutation_Insert{ + Insert: &spannerpb.Mutation_Write{ + Table: "my_table", + Columns: []string{"col1", "col2", "col3", "col4"}, + Values: []*structpb.ListValue{ + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "val1"}}, + {Kind: &structpb.Value_NumberValue{NumberValue: 3.14}}, + {Kind: &structpb.Value_NullValue{NullValue: structpb.NullValue_NULL_VALUE}}, + {Kind: &structpb.Value_BoolValue{BoolValue: true}}, + }}, + {Values: []*structpb.Value{ + {Kind: &structpb.Value_StringValue{StringValue: "val2"}}, + {Kind: &structpb.Value_NumberValue{NumberValue: 6.626}}, + {Kind: &structpb.Value_NullValue{NullValue: structpb.NullValue_NULL_VALUE}}, + {Kind: &structpb.Value_BoolValue{BoolValue: false}}, + }}, + }, + }, + }}, + }, + } + mutationBytes, err := proto.Marshal(mutations) + if err != nil { + t.Fatal(err) + } + pinner, code, id, length, ptr := WriteMutations(poolId, connId, mutationBytes) + verifyNonEmptyMessage(t, "WriteMutations", pinner, code, id, length, ptr) + defer Release(pinner) + + responseBytes := reflect.SliceAt(reflect.TypeOf(byte(0)), ptr, int(length)).Bytes() + response := &spannerpb.CommitResponse{} + if err := proto.Unmarshal(responseBytes, response); err != nil { + t.Fatal(err) + } + if response.CommitTimestamp == nil { + t.Fatal("CommitTimestamp is nil") + } + + requests := server.TestSpanner.DrainRequestsFromServer() + beginRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&spannerpb.BeginTransactionRequest{})) + if g, w := len(beginRequests), 1; g != w { + t.Fatalf("num begin requests mismatch\n Got: %v\nWant: %v", g, w) + } + commitRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&spannerpb.CommitRequest{})) + if g, w := len(commitRequests), 1; g != w { + t.Fatalf("num commit requests mismatch\n Got: %v\nWant: %v", g, w) + } + commitRequest := commitRequests[0].(*spannerpb.CommitRequest) + if g, w := len(commitRequest.Mutations), 1; g != w { + t.Fatalf("num mutations mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := commitRequest.Mutations[0].GetInsert().GetTable(), "my_table"; g != w { + t.Fatalf("insert table mismatch\n Got: %v\nWant: %v", g, w) + } + if g, w := len(commitRequest.Mutations[0].GetInsert().GetValues()), 2; g != w { + t.Fatalf("num rows mismatch\n Got: %v\nWant: %v", g, w) + } +} + +func TestExecute(t *testing.T) { + poolId, connId, server, teardown := setupTestConnection(t, databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL) + defer teardown() + + query := "select * from my_table" + _ = server.TestSpanner.PutStatementResult(query, &testutil.StatementResult{ + Type: testutil.StatementResultResultSet, + ResultSet: generateResultSet(5), + }) + + request := &spannerpb.ExecuteSqlRequest{ + Sql: query, + } + requestBytes, err := proto.Marshal(request) + if err != nil { + t.Fatal(err) + } + pinner, code, rowsId, length, ptr := Execute(poolId, connId, requestBytes) + verifyEmptyIdMessage(t, "Execute", pinner, code, rowsId, length, ptr) + + requests := server.TestSpanner.DrainRequestsFromServer() + executeRequests := testutil.RequestsOfType(requests, reflect.TypeOf(&spannerpb.ExecuteSqlRequest{})) + if g, w := len(executeRequests), 1; g != w { + t.Fatalf("num execute requests mismatch\n Got: %v\nWant: %v", g, w) + } +} + +func verifyEmptyIdMessage(t *testing.T, name string, pinner int64, code int32, id int64, length int32, ptr unsafe.Pointer) { + verifyEmptyMessage(t, name, pinner, code, length, ptr) + if id <= int64(0) { + t.Fatalf("%s returned zero or negative id\n Got: %v", name, id) + } +} + +func verifyEmptyMessage(t *testing.T, name string, pinner int64, code int32, length int32, ptr unsafe.Pointer) { + if pinner != int64(0) && code != int32(0) { + msg := unsafe.String((*byte)(ptr), length) + t.Fatalf("%s returned error: %v", name, msg) + } + + if g, w := pinner, int64(0); g != w { + t.Fatalf("%s returned non-zero pinner\n Got: %v\nWant: %v", name, g, w) + } + if g, w := code, int32(codes.OK); g != w { + t.Fatalf("%s returned non-OK code\n Got: %v\nWant: %d", name, g, w) + } + if g, w := length, int32(0); g != w { + t.Fatalf("%s returned non-empty length\n Got: %v\nWant: %v", name, g, w) + } + if g, w := ptr, unsafe.Pointer(nil); g != w { + t.Fatalf("%s returned non-nil pointer\n Got: %v\nWant: %v", name, g, w) + } +} + +func verifyNonEmptyMessage(t *testing.T, name string, pinner int64, code int32, id int64, length int32, ptr unsafe.Pointer) { + if pinner == int64(0) { + t.Fatalf("%s returned zero pinner", name) + } + if g, w := code, int32(codes.OK); g != w { + t.Fatalf("%s returned non-OK code\n Got: %v\nWant: %d", name, g, w) + } + if g, w := id, int64(0); g != w { + t.Fatalf("%s returned non-zero id\n Got: %v\nWant: %v", name, g, w) + } + if length == int32(0) { + t.Fatalf("%s returned empty length", name) + } + if ptr == unsafe.Pointer(nil) { + t.Fatalf("%s returned nil pointer", name) + } +} + +func generateResultSet(numRows int) *spannerpb.ResultSet { + res := &spannerpb.ResultSet{ + Metadata: &spannerpb.ResultSetMetadata{ + RowType: &spannerpb.StructType{ + Fields: []*spannerpb.StructType_Field{ + {Type: &spannerpb.Type{Code: spannerpb.TypeCode_STRING}, Name: "col1"}, + {Type: &spannerpb.Type{Code: spannerpb.TypeCode_STRING}, Name: "col2"}, + {Type: &spannerpb.Type{Code: spannerpb.TypeCode_STRING}, Name: "col3"}, + {Type: &spannerpb.Type{Code: spannerpb.TypeCode_STRING}, Name: "col4"}, + {Type: &spannerpb.Type{Code: spannerpb.TypeCode_STRING}, Name: "col5"}, + }, + }, + }, + } + rows := make([]*structpb.ListValue, 0, numRows) + for i := 0; i < numRows; i++ { + values := make([]*structpb.Value, 0, 5) + for j := 0; j < 5; j++ { + values = append(values, &structpb.Value{ + Kind: &structpb.Value_StringValue{StringValue: uuid.New().String()}, + }) + } + rows = append(rows, &structpb.ListValue{ + Values: values, + }) + } + res.Rows = rows + return res +} + +func setupMockServer(t *testing.T, dialect databasepb.DatabaseDialect) (server *testutil.MockedSpannerInMemTestServer, teardown func()) { + server, _, serverTeardown := testutil.NewMockedSpannerInMemTestServer(t) + server.SetupSelectDialectResult(dialect) + + return server, serverTeardown +} + +func setupTestConnection(t *testing.T, dialect databasepb.DatabaseDialect) (poolId int64, connId int64, server *testutil.MockedSpannerInMemTestServer, teardown func()) { + server, serverTeardown := setupMockServer(t, databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL) + + project := "my-project" + instance := "my-instance" + database := "my-database" + dsn := fmt.Sprintf("//%s/projects/%s/instances/%s/databases/%s;usePlainText=true", server.Address, project, instance, database) + _, code, poolId, _, _ := CreatePool(dsn) + if code != int32(codes.OK) { + t.Fatalf("CreatePool returned non-OK code\n Got: %v", code) + } + _, code, connId, _, _ = CreateConnection(poolId) + if code != int32(codes.OK) { + t.Fatalf("CreateConnection returned non-OK code\n Got: %v", code) + } + + return poolId, connId, server, func() { + CloseConnection(poolId, connId) + ClosePool(poolId) + serverTeardown() + } +} diff --git a/statements.go b/statements.go index 6920e7f2..f7232a7b 100644 --- a/statements.go +++ b/statements.go @@ -286,7 +286,7 @@ type executableCommitStatement struct { } func (s *executableCommitStatement) execContext(ctx context.Context, c *conn, opts *ExecOptions) (driver.Result, error) { - _, err := c.commit(ctx) + _, err := c.Commit(ctx) if err != nil { return nil, err } @@ -305,7 +305,7 @@ type executableRollbackStatement struct { } func (s *executableRollbackStatement) execContext(ctx context.Context, c *conn, opts *ExecOptions) (driver.Result, error) { - if err := c.rollback(ctx); err != nil { + if err := c.Rollback(ctx); err != nil { return nil, err } return driver.ResultNoRows, nil diff --git a/transaction.go b/transaction.go index dcc61d1b..af3c9e5c 100644 --- a/transaction.go +++ b/transaction.go @@ -44,7 +44,7 @@ type contextTransaction interface { Commit() error Rollback() error resetForRetry(ctx context.Context) error - Query(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (rowIterator, error) + Query(ctx context.Context, stmt spanner.Statement, stmtType parser.StatementType, execOptions *ExecOptions) (rowIterator, error) partitionQuery(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (driver.Rows, error) ExecContext(ctx context.Context, stmt spanner.Statement, statementInfo *parser.StatementInfo, options spanner.QueryOptions) (*result, error) @@ -67,6 +67,7 @@ var _ rowIterator = &readOnlyRowIterator{} type readOnlyRowIterator struct { *spanner.RowIterator + stmtType parser.StatementType } func (ri *readOnlyRowIterator) Next() (*spanner.Row, error) { @@ -82,12 +83,19 @@ func (ri *readOnlyRowIterator) Metadata() (*sppb.ResultSetMetadata, error) { } func (ri *readOnlyRowIterator) ResultSetStats() *sppb.ResultSetStats { + return createResultSetStats(ri.RowIterator, ri.stmtType) +} + +func createResultSetStats(it *spanner.RowIterator, stmtType parser.StatementType) *sppb.ResultSetStats { // TODO: The Spanner client library should offer an option to get the full // ResultSetStats, instead of only the RowCount and QueryPlan. - return &sppb.ResultSetStats{ - RowCount: &sppb.ResultSetStats_RowCountExact{RowCountExact: ri.RowIterator.RowCount}, - QueryPlan: ri.RowIterator.QueryPlan, + stats := &sppb.ResultSetStats{ + QueryPlan: it.QueryPlan, + } + if stmtType == parser.StatementTypeDml { + stats.RowCount = &sppb.ResultSetStats_RowCountExact{RowCountExact: it.RowCount} } + return stats } type txResult int @@ -135,7 +143,7 @@ func (tx *readOnlyTransaction) resetForRetry(ctx context.Context) error { return nil } -func (tx *readOnlyTransaction) Query(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (rowIterator, error) { +func (tx *readOnlyTransaction) Query(ctx context.Context, stmt spanner.Statement, stmtType parser.StatementType, execOptions *ExecOptions) (rowIterator, error) { tx.logger.DebugContext(ctx, "Query", "stmt", stmt.SQL) if execOptions.PartitionedQueryOptions.AutoPartitionQuery { if tx.boTx == nil { @@ -152,7 +160,7 @@ func (tx *readOnlyTransaction) Query(ctx context.Context, stmt spanner.Statement } return mi, nil } - return &readOnlyRowIterator{tx.roTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions)}, nil + return &readOnlyRowIterator{tx.roTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions), stmtType}, nil } func (tx *readOnlyTransaction) partitionQuery(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (driver.Rows, error) { @@ -456,7 +464,7 @@ func (tx *readWriteTransaction) resetForRetry(ctx context.Context) error { // Query executes a query using the read/write transaction and returns a // rowIterator that will automatically retry the read/write transaction if the // transaction is aborted during the query or while iterating the returned rows. -func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (rowIterator, error) { +func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statement, stmtType parser.StatementType, execOptions *ExecOptions) (rowIterator, error) { tx.logger.Debug("Query", "stmt", stmt.SQL) tx.active = true if err := tx.maybeRunAutoDmlBatch(ctx); err != nil { @@ -465,7 +473,7 @@ func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statemen // If internal retries have been disabled, we don't need to keep track of a // running checksum for all results that we have seen. if !tx.retryAborts() { - return &readOnlyRowIterator{tx.rwTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions)}, nil + return &readOnlyRowIterator{tx.rwTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions), stmtType}, nil } // If retries are enabled, we need to use a row iterator that will keep @@ -476,6 +484,7 @@ func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statemen ctx: ctx, tx: tx, stmt: stmt, + stmtType: stmtType, options: execOptions.QueryOptions, buffer: buffer, enc: gob.NewEncoder(buffer), diff --git a/wrapped_row_iterator.go b/wrapped_row_iterator.go index 4e99cff2..edc5e2aa 100644 --- a/wrapped_row_iterator.go +++ b/wrapped_row_iterator.go @@ -17,11 +17,13 @@ package spannerdriver import ( "cloud.google.com/go/spanner" "cloud.google.com/go/spanner/apiv1/spannerpb" + "github.com/googleapis/go-sql-spanner/parser" "google.golang.org/api/iterator" ) var _ rowIterator = &wrappedRowIterator{} +// wrappedRowIterator is used for DML statements that may or may not contain rows. type wrappedRowIterator struct { *spanner.RowIterator @@ -49,8 +51,6 @@ func (ri *wrappedRowIterator) Metadata() (*spannerpb.ResultSetMetadata, error) { } func (ri *wrappedRowIterator) ResultSetStats() *spannerpb.ResultSetStats { - return &spannerpb.ResultSetStats{ - RowCount: &spannerpb.ResultSetStats_RowCountExact{RowCountExact: ri.RowIterator.RowCount}, - QueryPlan: ri.RowIterator.QueryPlan, - } + // wrappedRowIterator is only used for DML statements. + return createResultSetStats(ri.RowIterator, parser.StatementTypeDml) }