aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/cloud.google.com/go/spanner
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cloud.google.com/go/spanner')
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go556
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go204
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go39
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go779
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go39
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go742
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go230
-rw-r--r--vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go896
-rw-r--r--vendor/cloud.google.com/go/spanner/backoff.go58
-rw-r--r--vendor/cloud.google.com/go/spanner/backoff_test.go62
-rw-r--r--vendor/cloud.google.com/go/spanner/client.go322
-rw-r--r--vendor/cloud.google.com/go/spanner/client_test.go50
-rw-r--r--vendor/cloud.google.com/go/spanner/doc.go311
-rw-r--r--vendor/cloud.google.com/go/spanner/errors.go108
-rw-r--r--vendor/cloud.google.com/go/spanner/examples_test.go536
-rw-r--r--vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go355
-rw-r--r--vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go255
-rw-r--r--vendor/cloud.google.com/go/spanner/key.go400
-rw-r--r--vendor/cloud.google.com/go/spanner/key_test.go373
-rw-r--r--vendor/cloud.google.com/go/spanner/mutation.go431
-rw-r--r--vendor/cloud.google.com/go/spanner/mutation_test.go543
-rw-r--r--vendor/cloud.google.com/go/spanner/protoutils.go113
-rw-r--r--vendor/cloud.google.com/go/spanner/read.go685
-rw-r--r--vendor/cloud.google.com/go/spanner/read_test.go1733
-rw-r--r--vendor/cloud.google.com/go/spanner/retry.go192
-rw-r--r--vendor/cloud.google.com/go/spanner/retry_test.go108
-rw-r--r--vendor/cloud.google.com/go/spanner/row.go308
-rw-r--r--vendor/cloud.google.com/go/spanner/row_test.go1775
-rw-r--r--vendor/cloud.google.com/go/spanner/session.go968
-rw-r--r--vendor/cloud.google.com/go/spanner/session_test.go792
-rw-r--r--vendor/cloud.google.com/go/spanner/spanner_test.go1234
-rw-r--r--vendor/cloud.google.com/go/spanner/statement.go78
-rw-r--r--vendor/cloud.google.com/go/spanner/statement_test.go64
-rw-r--r--vendor/cloud.google.com/go/spanner/timestampbound.go245
-rw-r--r--vendor/cloud.google.com/go/spanner/timestampbound_test.go208
-rw-r--r--vendor/cloud.google.com/go/spanner/transaction.go821
-rw-r--r--vendor/cloud.google.com/go/spanner/value.go1244
-rw-r--r--vendor/cloud.google.com/go/spanner/value_test.go611
38 files changed, 0 insertions, 18468 deletions
diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go
deleted file mode 100644
index 95d623059..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client.go
+++ /dev/null
@@ -1,556 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-package database
-
-import (
- "math"
- "time"
-
- "cloud.google.com/go/internal/version"
- "cloud.google.com/go/longrunning"
- lroauto "cloud.google.com/go/longrunning/autogen"
- gax "github.com/googleapis/gax-go"
- "golang.org/x/net/context"
- "google.golang.org/api/iterator"
- "google.golang.org/api/option"
- "google.golang.org/api/transport"
- iampb "google.golang.org/genproto/googleapis/iam/v1"
- longrunningpb "google.golang.org/genproto/googleapis/longrunning"
- databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
-)
-
-var (
- databaseAdminInstancePathTemplate = gax.MustCompilePathTemplate("projects/{project}/instances/{instance}")
- databaseAdminDatabasePathTemplate = gax.MustCompilePathTemplate("projects/{project}/instances/{instance}/databases/{database}")
-)
-
-// DatabaseAdminCallOptions contains the retry settings for each method of DatabaseAdminClient.
-type DatabaseAdminCallOptions struct {
- ListDatabases []gax.CallOption
- CreateDatabase []gax.CallOption
- GetDatabase []gax.CallOption
- UpdateDatabaseDdl []gax.CallOption
- DropDatabase []gax.CallOption
- GetDatabaseDdl []gax.CallOption
- SetIamPolicy []gax.CallOption
- GetIamPolicy []gax.CallOption
- TestIamPermissions []gax.CallOption
-}
-
-func defaultDatabaseAdminClientOptions() []option.ClientOption {
- return []option.ClientOption{
- option.WithEndpoint("spanner.googleapis.com:443"),
- option.WithScopes(DefaultAuthScopes()...),
- }
-}
-
-func defaultDatabaseAdminCallOptions() *DatabaseAdminCallOptions {
- retry := map[[2]string][]gax.CallOption{
- {"default", "idempotent"}: {
- gax.WithRetry(func() gax.Retryer {
- return gax.OnCodes([]codes.Code{
- codes.DeadlineExceeded,
- codes.Unavailable,
- }, gax.Backoff{
- Initial: 1000 * time.Millisecond,
- Max: 32000 * time.Millisecond,
- Multiplier: 1.3,
- })
- }),
- },
- {"default", "non_idempotent"}: {
- gax.WithRetry(func() gax.Retryer {
- return gax.OnCodes([]codes.Code{
- codes.Unavailable,
- }, gax.Backoff{
- Initial: 1000 * time.Millisecond,
- Max: 32000 * time.Millisecond,
- Multiplier: 1.3,
- })
- }),
- },
- }
- return &DatabaseAdminCallOptions{
- ListDatabases: retry[[2]string{"default", "idempotent"}],
- CreateDatabase: retry[[2]string{"default", "non_idempotent"}],
- GetDatabase: retry[[2]string{"default", "idempotent"}],
- UpdateDatabaseDdl: retry[[2]string{"default", "idempotent"}],
- DropDatabase: retry[[2]string{"default", "idempotent"}],
- GetDatabaseDdl: retry[[2]string{"default", "idempotent"}],
- SetIamPolicy: retry[[2]string{"default", "non_idempotent"}],
- GetIamPolicy: retry[[2]string{"default", "idempotent"}],
- TestIamPermissions: retry[[2]string{"default", "non_idempotent"}],
- }
-}
-
-// DatabaseAdminClient is a client for interacting with Cloud Spanner Database Admin API.
-type DatabaseAdminClient struct {
- // The connection to the service.
- conn *grpc.ClientConn
-
- // The gRPC API client.
- databaseAdminClient databasepb.DatabaseAdminClient
-
- // LROClient is used internally to handle longrunning operations.
- // It is exposed so that its CallOptions can be modified if required.
- // Users should not Close this client.
- LROClient *lroauto.OperationsClient
-
- // The call options for this service.
- CallOptions *DatabaseAdminCallOptions
-
- // The metadata to be sent with each request.
- xGoogHeader []string
-}
-
-// NewDatabaseAdminClient creates a new database admin client.
-//
-// Cloud Spanner Database Admin API
-//
-// The Cloud Spanner Database Admin API can be used to create, drop, and
-// list databases. It also enables updating the schema of pre-existing
-// databases.
-func NewDatabaseAdminClient(ctx context.Context, opts ...option.ClientOption) (*DatabaseAdminClient, error) {
- conn, err := transport.DialGRPC(ctx, append(defaultDatabaseAdminClientOptions(), opts...)...)
- if err != nil {
- return nil, err
- }
- c := &DatabaseAdminClient{
- conn: conn,
- CallOptions: defaultDatabaseAdminCallOptions(),
-
- databaseAdminClient: databasepb.NewDatabaseAdminClient(conn),
- }
- c.SetGoogleClientInfo()
-
- c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn))
- if err != nil {
- // This error "should not happen", since we are just reusing old connection
- // and never actually need to dial.
- // If this does happen, we could leak conn. However, we cannot close conn:
- // If the user invoked the function with option.WithGRPCConn,
- // we would close a connection that's still in use.
- // TODO(pongad): investigate error conditions.
- return nil, err
- }
- return c, nil
-}
-
-// Connection returns the client's connection to the API service.
-func (c *DatabaseAdminClient) Connection() *grpc.ClientConn {
- return c.conn
-}
-
-// Close closes the connection to the API service. The user should invoke this when
-// the client is no longer required.
-func (c *DatabaseAdminClient) Close() error {
- return c.conn.Close()
-}
-
-// SetGoogleClientInfo sets the name and version of the application in
-// the `x-goog-api-client` header passed on each request. Intended for
-// use by Google-written clients.
-func (c *DatabaseAdminClient) SetGoogleClientInfo(keyval ...string) {
- kv := append([]string{"gl-go", version.Go()}, keyval...)
- kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version)
- c.xGoogHeader = []string{gax.XGoogHeader(kv...)}
-}
-
-// DatabaseAdminInstancePath returns the path for the instance resource.
-func DatabaseAdminInstancePath(project, instance string) string {
- path, err := databaseAdminInstancePathTemplate.Render(map[string]string{
- "project": project,
- "instance": instance,
- })
- if err != nil {
- panic(err)
- }
- return path
-}
-
-// DatabaseAdminDatabasePath returns the path for the database resource.
-func DatabaseAdminDatabasePath(project, instance, database string) string {
- path, err := databaseAdminDatabasePathTemplate.Render(map[string]string{
- "project": project,
- "instance": instance,
- "database": database,
- })
- if err != nil {
- panic(err)
- }
- return path
-}
-
-// ListDatabases lists Cloud Spanner databases.
-func (c *DatabaseAdminClient) ListDatabases(ctx context.Context, req *databasepb.ListDatabasesRequest, opts ...gax.CallOption) *DatabaseIterator {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.ListDatabases[0:len(c.CallOptions.ListDatabases):len(c.CallOptions.ListDatabases)], opts...)
- it := &DatabaseIterator{}
- it.InternalFetch = func(pageSize int, pageToken string) ([]*databasepb.Database, string, error) {
- var resp *databasepb.ListDatabasesResponse
- req.PageToken = pageToken
- if pageSize > math.MaxInt32 {
- req.PageSize = math.MaxInt32
- } else {
- req.PageSize = int32(pageSize)
- }
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.ListDatabases(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, "", err
- }
- return resp.Databases, resp.NextPageToken, nil
- }
- fetch := func(pageSize int, pageToken string) (string, error) {
- items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
- if err != nil {
- return "", err
- }
- it.items = append(it.items, items...)
- return nextPageToken, nil
- }
- it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
- return it
-}
-
-// CreateDatabase 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 `<database_name>/operations/<operation_id>` 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.
-func (c *DatabaseAdminClient) CreateDatabase(ctx context.Context, req *databasepb.CreateDatabaseRequest, opts ...gax.CallOption) (*CreateDatabaseOperation, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.CreateDatabase[0:len(c.CallOptions.CreateDatabase):len(c.CallOptions.CreateDatabase)], opts...)
- var resp *longrunningpb.Operation
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.CreateDatabase(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return &CreateDatabaseOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, resp),
- }, nil
-}
-
-// GetDatabase gets the state of a Cloud Spanner database.
-func (c *DatabaseAdminClient) GetDatabase(ctx context.Context, req *databasepb.GetDatabaseRequest, opts ...gax.CallOption) (*databasepb.Database, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.GetDatabase[0:len(c.CallOptions.GetDatabase):len(c.CallOptions.GetDatabase)], opts...)
- var resp *databasepb.Database
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.GetDatabase(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// UpdateDatabaseDdl 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 `<database_name>/operations/<operation_id>` 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.
-func (c *DatabaseAdminClient) UpdateDatabaseDdl(ctx context.Context, req *databasepb.UpdateDatabaseDdlRequest, opts ...gax.CallOption) (*UpdateDatabaseDdlOperation, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.UpdateDatabaseDdl[0:len(c.CallOptions.UpdateDatabaseDdl):len(c.CallOptions.UpdateDatabaseDdl)], opts...)
- var resp *longrunningpb.Operation
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.UpdateDatabaseDdl(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return &UpdateDatabaseDdlOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, resp),
- }, nil
-}
-
-// DropDatabase drops (aka deletes) a Cloud Spanner database.
-func (c *DatabaseAdminClient) DropDatabase(ctx context.Context, req *databasepb.DropDatabaseRequest, opts ...gax.CallOption) error {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.DropDatabase[0:len(c.CallOptions.DropDatabase):len(c.CallOptions.DropDatabase)], opts...)
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- _, err = c.databaseAdminClient.DropDatabase(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- return err
-}
-
-// GetDatabaseDdl 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.
-func (c *DatabaseAdminClient) GetDatabaseDdl(ctx context.Context, req *databasepb.GetDatabaseDdlRequest, opts ...gax.CallOption) (*databasepb.GetDatabaseDdlResponse, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.GetDatabaseDdl[0:len(c.CallOptions.GetDatabaseDdl):len(c.CallOptions.GetDatabaseDdl)], opts...)
- var resp *databasepb.GetDatabaseDdlResponse
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.GetDatabaseDdl(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// SetIamPolicy sets the access control policy on a database resource. Replaces any
-// existing policy.
-//
-// Authorization requires `spanner.databases.setIamPolicy` permission on
-// [resource][google.iam.v1.SetIamPolicyRequest.resource].
-func (c *DatabaseAdminClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.SetIamPolicy[0:len(c.CallOptions.SetIamPolicy):len(c.CallOptions.SetIamPolicy)], opts...)
- var resp *iampb.Policy
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.SetIamPolicy(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// GetIamPolicy gets the access control policy for a database resource. Returns an empty
-// policy if a database exists but does not have a policy set.
-//
-// Authorization requires `spanner.databases.getIamPolicy` permission on
-// [resource][google.iam.v1.GetIamPolicyRequest.resource].
-func (c *DatabaseAdminClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.GetIamPolicy[0:len(c.CallOptions.GetIamPolicy):len(c.CallOptions.GetIamPolicy)], opts...)
- var resp *iampb.Policy
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.GetIamPolicy(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// TestIamPermissions returns permissions that the caller has on the specified database 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.
-func (c *DatabaseAdminClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.TestIamPermissions[0:len(c.CallOptions.TestIamPermissions):len(c.CallOptions.TestIamPermissions)], opts...)
- var resp *iampb.TestIamPermissionsResponse
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.databaseAdminClient.TestIamPermissions(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// DatabaseIterator manages a stream of *databasepb.Database.
-type DatabaseIterator struct {
- items []*databasepb.Database
- pageInfo *iterator.PageInfo
- nextFunc func() error
-
- // InternalFetch is for use by the Google Cloud Libraries only.
- // It is not part of the stable interface of this package.
- //
- // InternalFetch returns results from a single call to the underlying RPC.
- // The number of results is no greater than pageSize.
- // If there are no more results, nextPageToken is empty and err is nil.
- InternalFetch func(pageSize int, pageToken string) (results []*databasepb.Database, nextPageToken string, err error)
-}
-
-// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
-func (it *DatabaseIterator) PageInfo() *iterator.PageInfo {
- return it.pageInfo
-}
-
-// Next returns the next result. Its second return value is iterator.Done if there are no more
-// results. Once Next returns Done, all subsequent calls will return Done.
-func (it *DatabaseIterator) Next() (*databasepb.Database, error) {
- var item *databasepb.Database
- if err := it.nextFunc(); err != nil {
- return item, err
- }
- item = it.items[0]
- it.items = it.items[1:]
- return item, nil
-}
-
-func (it *DatabaseIterator) bufLen() int {
- return len(it.items)
-}
-
-func (it *DatabaseIterator) takeBuf() interface{} {
- b := it.items
- it.items = nil
- return b
-}
-
-// CreateDatabaseOperation manages a long-running operation from CreateDatabase.
-type CreateDatabaseOperation struct {
- lro *longrunning.Operation
-}
-
-// CreateDatabaseOperation returns a new CreateDatabaseOperation from a given name.
-// The name must be that of a previously created CreateDatabaseOperation, possibly from a different process.
-func (c *DatabaseAdminClient) CreateDatabaseOperation(name string) *CreateDatabaseOperation {
- return &CreateDatabaseOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}),
- }
-}
-
-// Wait blocks until the long-running operation is completed, returning the response and any errors encountered.
-//
-// See documentation of Poll for error-handling information.
-func (op *CreateDatabaseOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*databasepb.Database, error) {
- var resp databasepb.Database
- if err := op.lro.Wait(ctx, &resp, opts...); err != nil {
- return nil, err
- }
- return &resp, nil
-}
-
-// Poll fetches the latest state of the long-running operation.
-//
-// Poll also fetches the latest metadata, which can be retrieved by Metadata.
-//
-// If Poll fails, the error is returned and op is unmodified. If Poll succeeds and
-// the operation has completed with failure, the error is returned and op.Done will return true.
-// If Poll succeeds and the operation has completed successfully,
-// op.Done will return true, and the response of the operation is returned.
-// If Poll succeeds and the operation has not completed, the returned response and error are both nil.
-func (op *CreateDatabaseOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*databasepb.Database, error) {
- var resp databasepb.Database
- if err := op.lro.Poll(ctx, &resp, opts...); err != nil {
- return nil, err
- }
- if !op.Done() {
- return nil, nil
- }
- return &resp, nil
-}
-
-// Metadata returns metadata associated with the long-running operation.
-// Metadata itself does not contact the server, but Poll does.
-// To get the latest metadata, call this method after a successful call to Poll.
-// If the metadata is not available, the returned metadata and error are both nil.
-func (op *CreateDatabaseOperation) Metadata() (*databasepb.CreateDatabaseMetadata, error) {
- var meta databasepb.CreateDatabaseMetadata
- if err := op.lro.Metadata(&meta); err == longrunning.ErrNoMetadata {
- return nil, nil
- } else if err != nil {
- return nil, err
- }
- return &meta, nil
-}
-
-// Done reports whether the long-running operation has completed.
-func (op *CreateDatabaseOperation) Done() bool {
- return op.lro.Done()
-}
-
-// Name returns the name of the long-running operation.
-// The name is assigned by the server and is unique within the service from which the operation is created.
-func (op *CreateDatabaseOperation) Name() string {
- return op.lro.Name()
-}
-
-// UpdateDatabaseDdlOperation manages a long-running operation from UpdateDatabaseDdl.
-type UpdateDatabaseDdlOperation struct {
- lro *longrunning.Operation
-}
-
-// UpdateDatabaseDdlOperation returns a new UpdateDatabaseDdlOperation from a given name.
-// The name must be that of a previously created UpdateDatabaseDdlOperation, possibly from a different process.
-func (c *DatabaseAdminClient) UpdateDatabaseDdlOperation(name string) *UpdateDatabaseDdlOperation {
- return &UpdateDatabaseDdlOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}),
- }
-}
-
-// Wait blocks until the long-running operation is completed, returning any error encountered.
-//
-// See documentation of Poll for error-handling information.
-func (op *UpdateDatabaseDdlOperation) Wait(ctx context.Context, opts ...gax.CallOption) error {
- return op.lro.Wait(ctx, nil, opts...)
-}
-
-// Poll fetches the latest state of the long-running operation.
-//
-// Poll also fetches the latest metadata, which can be retrieved by Metadata.
-//
-// If Poll fails, the error is returned and op is unmodified. If Poll succeeds and
-// the operation has completed with failure, the error is returned and op.Done will return true.
-// If Poll succeeds and the operation has completed successfully, op.Done will return true.
-func (op *UpdateDatabaseDdlOperation) Poll(ctx context.Context, opts ...gax.CallOption) error {
- return op.lro.Poll(ctx, nil, opts...)
-}
-
-// Metadata returns metadata associated with the long-running operation.
-// Metadata itself does not contact the server, but Poll does.
-// To get the latest metadata, call this method after a successful call to Poll.
-// If the metadata is not available, the returned metadata and error are both nil.
-func (op *UpdateDatabaseDdlOperation) Metadata() (*databasepb.UpdateDatabaseDdlMetadata, error) {
- var meta databasepb.UpdateDatabaseDdlMetadata
- if err := op.lro.Metadata(&meta); err == longrunning.ErrNoMetadata {
- return nil, nil
- } else if err != nil {
- return nil, err
- }
- return &meta, nil
-}
-
-// Done reports whether the long-running operation has completed.
-func (op *UpdateDatabaseDdlOperation) Done() bool {
- return op.lro.Done()
-}
-
-// Name returns the name of the long-running operation.
-// The name is assigned by the server and is unique within the service from which the operation is created.
-func (op *UpdateDatabaseDdlOperation) Name() string {
- return op.lro.Name()
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go
deleted file mode 100644
index 0769d1193..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/database_admin_client_example_test.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-package database_test
-
-import (
- "cloud.google.com/go/spanner/admin/database/apiv1"
- "golang.org/x/net/context"
- iampb "google.golang.org/genproto/googleapis/iam/v1"
- databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
-)
-
-func ExampleNewDatabaseAdminClient() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use client.
- _ = c
-}
-
-func ExampleDatabaseAdminClient_ListDatabases() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &databasepb.ListDatabasesRequest{
- // TODO: Fill request struct fields.
- }
- it := c.ListDatabases(ctx, req)
- for {
- resp, err := it.Next()
- if err != nil {
- // TODO: Handle error.
- break
- }
- // TODO: Use resp.
- _ = resp
- }
-}
-
-func ExampleDatabaseAdminClient_CreateDatabase() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &databasepb.CreateDatabaseRequest{
- // TODO: Fill request struct fields.
- }
- op, err := c.CreateDatabase(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
-
- resp, err := op.Wait(ctx)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleDatabaseAdminClient_GetDatabase() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &databasepb.GetDatabaseRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.GetDatabase(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleDatabaseAdminClient_UpdateDatabaseDdl() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &databasepb.UpdateDatabaseDdlRequest{
- // TODO: Fill request struct fields.
- }
- op, err := c.UpdateDatabaseDdl(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
-
- err = op.Wait(ctx)
- // TODO: Handle error.
-}
-
-func ExampleDatabaseAdminClient_DropDatabase() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &databasepb.DropDatabaseRequest{
- // TODO: Fill request struct fields.
- }
- err = c.DropDatabase(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-func ExampleDatabaseAdminClient_GetDatabaseDdl() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &databasepb.GetDatabaseDdlRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.GetDatabaseDdl(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleDatabaseAdminClient_SetIamPolicy() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &iampb.SetIamPolicyRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.SetIamPolicy(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleDatabaseAdminClient_GetIamPolicy() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &iampb.GetIamPolicyRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.GetIamPolicy(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleDatabaseAdminClient_TestIamPermissions() {
- ctx := context.Background()
- c, err := database.NewDatabaseAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &iampb.TestIamPermissionsRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.TestIamPermissions(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go
deleted file mode 100644
index 46eaaae73..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/doc.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-// Package database is an experimental, auto-generated package for the
-// database API.
-//
-package database // import "cloud.google.com/go/spanner/admin/database/apiv1"
-
-import (
- "golang.org/x/net/context"
- "google.golang.org/grpc/metadata"
-)
-
-func insertXGoog(ctx context.Context, val []string) context.Context {
- md, _ := metadata.FromOutgoingContext(ctx)
- md = md.Copy()
- md["x-goog-api-client"] = val
- return metadata.NewOutgoingContext(ctx, md)
-}
-
-func DefaultAuthScopes() []string {
- return []string{
- "https://www.googleapis.com/auth/cloud-platform",
- "https://www.googleapis.com/auth/spanner.admin",
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go b/vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go
deleted file mode 100644
index accd30cb3..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/database/apiv1/mock_test.go
+++ /dev/null
@@ -1,779 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-package database
-
-import (
- emptypb "github.com/golang/protobuf/ptypes/empty"
- iampb "google.golang.org/genproto/googleapis/iam/v1"
- longrunningpb "google.golang.org/genproto/googleapis/longrunning"
- databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
-)
-
-import (
- "flag"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "strings"
- "testing"
-
- "github.com/golang/protobuf/proto"
- "github.com/golang/protobuf/ptypes"
- "golang.org/x/net/context"
- "google.golang.org/api/option"
- status "google.golang.org/genproto/googleapis/rpc/status"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-var _ = io.EOF
-var _ = ptypes.MarshalAny
-var _ status.Status
-
-type mockDatabaseAdminServer struct {
- // Embed for forward compatibility.
- // Tests will keep working if more methods are added
- // in the future.
- databasepb.DatabaseAdminServer
-
- reqs []proto.Message
-
- // If set, all calls return this error.
- err error
-
- // responses to return if err == nil
- resps []proto.Message
-}
-
-func (s *mockDatabaseAdminServer) ListDatabases(ctx context.Context, req *databasepb.ListDatabasesRequest) (*databasepb.ListDatabasesResponse, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*databasepb.ListDatabasesResponse), nil
-}
-
-func (s *mockDatabaseAdminServer) CreateDatabase(ctx context.Context, req *databasepb.CreateDatabaseRequest) (*longrunningpb.Operation, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*longrunningpb.Operation), nil
-}
-
-func (s *mockDatabaseAdminServer) GetDatabase(ctx context.Context, req *databasepb.GetDatabaseRequest) (*databasepb.Database, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*databasepb.Database), nil
-}
-
-func (s *mockDatabaseAdminServer) UpdateDatabaseDdl(ctx context.Context, req *databasepb.UpdateDatabaseDdlRequest) (*longrunningpb.Operation, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*longrunningpb.Operation), nil
-}
-
-func (s *mockDatabaseAdminServer) DropDatabase(ctx context.Context, req *databasepb.DropDatabaseRequest) (*emptypb.Empty, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*emptypb.Empty), nil
-}
-
-func (s *mockDatabaseAdminServer) GetDatabaseDdl(ctx context.Context, req *databasepb.GetDatabaseDdlRequest) (*databasepb.GetDatabaseDdlResponse, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*databasepb.GetDatabaseDdlResponse), nil
-}
-
-func (s *mockDatabaseAdminServer) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest) (*iampb.Policy, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*iampb.Policy), nil
-}
-
-func (s *mockDatabaseAdminServer) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest) (*iampb.Policy, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*iampb.Policy), nil
-}
-
-func (s *mockDatabaseAdminServer) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest) (*iampb.TestIamPermissionsResponse, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*iampb.TestIamPermissionsResponse), nil
-}
-
-// clientOpt is the option tests should use to connect to the test server.
-// It is initialized by TestMain.
-var clientOpt option.ClientOption
-
-var (
- mockDatabaseAdmin mockDatabaseAdminServer
-)
-
-func TestMain(m *testing.M) {
- flag.Parse()
-
- serv := grpc.NewServer()
- databasepb.RegisterDatabaseAdminServer(serv, &mockDatabaseAdmin)
-
- lis, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- log.Fatal(err)
- }
- go serv.Serve(lis)
-
- conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure())
- if err != nil {
- log.Fatal(err)
- }
- clientOpt = option.WithGRPCConn(conn)
-
- os.Exit(m.Run())
-}
-
-func TestDatabaseAdminListDatabases(t *testing.T) {
- var nextPageToken string = ""
- var databasesElement *databasepb.Database = &databasepb.Database{}
- var databases = []*databasepb.Database{databasesElement}
- var expectedResponse = &databasepb.ListDatabasesResponse{
- NextPageToken: nextPageToken,
- Databases: databases,
- }
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &databasepb.ListDatabasesRequest{
- Parent: formattedParent,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.ListDatabases(context.Background(), request).Next()
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- want := (interface{})(expectedResponse.Databases[0])
- got := (interface{})(resp)
- var ok bool
-
- switch want := (want).(type) {
- case proto.Message:
- ok = proto.Equal(want, got.(proto.Message))
- default:
- ok = want == got
- }
- if !ok {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminListDatabasesError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &databasepb.ListDatabasesRequest{
- Parent: formattedParent,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.ListDatabases(context.Background(), request).Next()
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestDatabaseAdminCreateDatabase(t *testing.T) {
- var name string = "name3373707"
- var expectedResponse = &databasepb.Database{
- Name: name,
- }
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- any, err := ptypes.MarshalAny(expectedResponse)
- if err != nil {
- t.Fatal(err)
- }
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Response{Response: any},
- })
-
- var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var createStatement string = "createStatement552974828"
- var request = &databasepb.CreateDatabaseRequest{
- Parent: formattedParent,
- CreateStatement: createStatement,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.CreateDatabase(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- resp, err := respLRO.Wait(context.Background())
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminCreateDatabaseError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Error{
- Error: &status.Status{
- Code: int32(errCode),
- Message: "test error",
- },
- },
- })
-
- var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var createStatement string = "createStatement552974828"
- var request = &databasepb.CreateDatabaseRequest{
- Parent: formattedParent,
- CreateStatement: createStatement,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.CreateDatabase(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- resp, err := respLRO.Wait(context.Background())
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestDatabaseAdminGetDatabase(t *testing.T) {
- var name2 string = "name2-1052831874"
- var expectedResponse = &databasepb.Database{
- Name: name2,
- }
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedName string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &databasepb.GetDatabaseRequest{
- Name: formattedName,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetDatabase(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminGetDatabaseError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedName string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &databasepb.GetDatabaseRequest{
- Name: formattedName,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetDatabase(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestDatabaseAdminUpdateDatabaseDdl(t *testing.T) {
- var expectedResponse *emptypb.Empty = &emptypb.Empty{}
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- any, err := ptypes.MarshalAny(expectedResponse)
- if err != nil {
- t.Fatal(err)
- }
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Response{Response: any},
- })
-
- var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var statements []string = nil
- var request = &databasepb.UpdateDatabaseDdlRequest{
- Database: formattedDatabase,
- Statements: statements,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.UpdateDatabaseDdl(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- err = respLRO.Wait(context.Background())
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
-}
-
-func TestDatabaseAdminUpdateDatabaseDdlError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Error{
- Error: &status.Status{
- Code: int32(errCode),
- Message: "test error",
- },
- },
- })
-
- var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var statements []string = nil
- var request = &databasepb.UpdateDatabaseDdlRequest{
- Database: formattedDatabase,
- Statements: statements,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.UpdateDatabaseDdl(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- err = respLRO.Wait(context.Background())
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
-}
-func TestDatabaseAdminDropDatabase(t *testing.T) {
- var expectedResponse *emptypb.Empty = &emptypb.Empty{}
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &databasepb.DropDatabaseRequest{
- Database: formattedDatabase,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- err = c.DropDatabase(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
-}
-
-func TestDatabaseAdminDropDatabaseError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &databasepb.DropDatabaseRequest{
- Database: formattedDatabase,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- err = c.DropDatabase(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
-}
-func TestDatabaseAdminGetDatabaseDdl(t *testing.T) {
- var expectedResponse *databasepb.GetDatabaseDdlResponse = &databasepb.GetDatabaseDdlResponse{}
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &databasepb.GetDatabaseDdlRequest{
- Database: formattedDatabase,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetDatabaseDdl(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminGetDatabaseDdlError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &databasepb.GetDatabaseDdlRequest{
- Database: formattedDatabase,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetDatabaseDdl(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestDatabaseAdminSetIamPolicy(t *testing.T) {
- var version int32 = 351608024
- var etag []byte = []byte("21")
- var expectedResponse = &iampb.Policy{
- Version: version,
- Etag: etag,
- }
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var policy *iampb.Policy = &iampb.Policy{}
- var request = &iampb.SetIamPolicyRequest{
- Resource: formattedResource,
- Policy: policy,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.SetIamPolicy(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminSetIamPolicyError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var policy *iampb.Policy = &iampb.Policy{}
- var request = &iampb.SetIamPolicyRequest{
- Resource: formattedResource,
- Policy: policy,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.SetIamPolicy(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestDatabaseAdminGetIamPolicy(t *testing.T) {
- var version int32 = 351608024
- var etag []byte = []byte("21")
- var expectedResponse = &iampb.Policy{
- Version: version,
- Etag: etag,
- }
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &iampb.GetIamPolicyRequest{
- Resource: formattedResource,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetIamPolicy(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminGetIamPolicyError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var request = &iampb.GetIamPolicyRequest{
- Resource: formattedResource,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetIamPolicy(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestDatabaseAdminTestIamPermissions(t *testing.T) {
- var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{}
-
- mockDatabaseAdmin.err = nil
- mockDatabaseAdmin.reqs = nil
-
- mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
-
- var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var permissions []string = nil
- var request = &iampb.TestIamPermissionsRequest{
- Resource: formattedResource,
- Permissions: permissions,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.TestIamPermissions(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockDatabaseAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestDatabaseAdminTestIamPermissionsError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockDatabaseAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
- var permissions []string = nil
- var request = &iampb.TestIamPermissionsRequest{
- Resource: formattedResource,
- Permissions: permissions,
- }
-
- c, err := NewDatabaseAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.TestIamPermissions(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go
deleted file mode 100644
index b0c982123..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/doc.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-// Package instance is an experimental, auto-generated package for the
-// instance API.
-//
-package instance // import "cloud.google.com/go/spanner/admin/instance/apiv1"
-
-import (
- "golang.org/x/net/context"
- "google.golang.org/grpc/metadata"
-)
-
-func insertXGoog(ctx context.Context, val []string) context.Context {
- md, _ := metadata.FromOutgoingContext(ctx)
- md = md.Copy()
- md["x-goog-api-client"] = val
- return metadata.NewOutgoingContext(ctx, md)
-}
-
-func DefaultAuthScopes() []string {
- return []string{
- "https://www.googleapis.com/auth/cloud-platform",
- "https://www.googleapis.com/auth/spanner.admin",
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go
deleted file mode 100644
index 138e813f2..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client.go
+++ /dev/null
@@ -1,742 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-package instance
-
-import (
- "math"
- "time"
-
- "cloud.google.com/go/internal/version"
- "cloud.google.com/go/longrunning"
- lroauto "cloud.google.com/go/longrunning/autogen"
- gax "github.com/googleapis/gax-go"
- "golang.org/x/net/context"
- "google.golang.org/api/iterator"
- "google.golang.org/api/option"
- "google.golang.org/api/transport"
- iampb "google.golang.org/genproto/googleapis/iam/v1"
- longrunningpb "google.golang.org/genproto/googleapis/longrunning"
- instancepb "google.golang.org/genproto/googleapis/spanner/admin/instance/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
-)
-
-var (
- instanceAdminProjectPathTemplate = gax.MustCompilePathTemplate("projects/{project}")
- instanceAdminInstanceConfigPathTemplate = gax.MustCompilePathTemplate("projects/{project}/instanceConfigs/{instance_config}")
- instanceAdminInstancePathTemplate = gax.MustCompilePathTemplate("projects/{project}/instances/{instance}")
-)
-
-// InstanceAdminCallOptions contains the retry settings for each method of InstanceAdminClient.
-type InstanceAdminCallOptions struct {
- ListInstanceConfigs []gax.CallOption
- GetInstanceConfig []gax.CallOption
- ListInstances []gax.CallOption
- GetInstance []gax.CallOption
- CreateInstance []gax.CallOption
- UpdateInstance []gax.CallOption
- DeleteInstance []gax.CallOption
- SetIamPolicy []gax.CallOption
- GetIamPolicy []gax.CallOption
- TestIamPermissions []gax.CallOption
-}
-
-func defaultInstanceAdminClientOptions() []option.ClientOption {
- return []option.ClientOption{
- option.WithEndpoint("spanner.googleapis.com:443"),
- option.WithScopes(DefaultAuthScopes()...),
- }
-}
-
-func defaultInstanceAdminCallOptions() *InstanceAdminCallOptions {
- retry := map[[2]string][]gax.CallOption{
- {"default", "idempotent"}: {
- gax.WithRetry(func() gax.Retryer {
- return gax.OnCodes([]codes.Code{
- codes.DeadlineExceeded,
- codes.Unavailable,
- }, gax.Backoff{
- Initial: 1000 * time.Millisecond,
- Max: 32000 * time.Millisecond,
- Multiplier: 1.3,
- })
- }),
- },
- {"default", "non_idempotent"}: {
- gax.WithRetry(func() gax.Retryer {
- return gax.OnCodes([]codes.Code{
- codes.Unavailable,
- }, gax.Backoff{
- Initial: 1000 * time.Millisecond,
- Max: 32000 * time.Millisecond,
- Multiplier: 1.3,
- })
- }),
- },
- }
- return &InstanceAdminCallOptions{
- ListInstanceConfigs: retry[[2]string{"default", "idempotent"}],
- GetInstanceConfig: retry[[2]string{"default", "idempotent"}],
- ListInstances: retry[[2]string{"default", "idempotent"}],
- GetInstance: retry[[2]string{"default", "idempotent"}],
- CreateInstance: retry[[2]string{"default", "non_idempotent"}],
- UpdateInstance: retry[[2]string{"default", "non_idempotent"}],
- DeleteInstance: retry[[2]string{"default", "idempotent"}],
- SetIamPolicy: retry[[2]string{"default", "non_idempotent"}],
- GetIamPolicy: retry[[2]string{"default", "idempotent"}],
- TestIamPermissions: retry[[2]string{"default", "non_idempotent"}],
- }
-}
-
-// InstanceAdminClient is a client for interacting with Cloud Spanner Instance Admin API.
-type InstanceAdminClient struct {
- // The connection to the service.
- conn *grpc.ClientConn
-
- // The gRPC API client.
- instanceAdminClient instancepb.InstanceAdminClient
-
- // LROClient is used internally to handle longrunning operations.
- // It is exposed so that its CallOptions can be modified if required.
- // Users should not Close this client.
- LROClient *lroauto.OperationsClient
-
- // The call options for this service.
- CallOptions *InstanceAdminCallOptions
-
- // The metadata to be sent with each request.
- xGoogHeader []string
-}
-
-// NewInstanceAdminClient creates a new instance admin client.
-//
-// 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.
-func NewInstanceAdminClient(ctx context.Context, opts ...option.ClientOption) (*InstanceAdminClient, error) {
- conn, err := transport.DialGRPC(ctx, append(defaultInstanceAdminClientOptions(), opts...)...)
- if err != nil {
- return nil, err
- }
- c := &InstanceAdminClient{
- conn: conn,
- CallOptions: defaultInstanceAdminCallOptions(),
-
- instanceAdminClient: instancepb.NewInstanceAdminClient(conn),
- }
- c.SetGoogleClientInfo()
-
- c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn))
- if err != nil {
- // This error "should not happen", since we are just reusing old connection
- // and never actually need to dial.
- // If this does happen, we could leak conn. However, we cannot close conn:
- // If the user invoked the function with option.WithGRPCConn,
- // we would close a connection that's still in use.
- // TODO(pongad): investigate error conditions.
- return nil, err
- }
- return c, nil
-}
-
-// Connection returns the client's connection to the API service.
-func (c *InstanceAdminClient) Connection() *grpc.ClientConn {
- return c.conn
-}
-
-// Close closes the connection to the API service. The user should invoke this when
-// the client is no longer required.
-func (c *InstanceAdminClient) Close() error {
- return c.conn.Close()
-}
-
-// SetGoogleClientInfo sets the name and version of the application in
-// the `x-goog-api-client` header passed on each request. Intended for
-// use by Google-written clients.
-func (c *InstanceAdminClient) SetGoogleClientInfo(keyval ...string) {
- kv := append([]string{"gl-go", version.Go()}, keyval...)
- kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version)
- c.xGoogHeader = []string{gax.XGoogHeader(kv...)}
-}
-
-// InstanceAdminProjectPath returns the path for the project resource.
-func InstanceAdminProjectPath(project string) string {
- path, err := instanceAdminProjectPathTemplate.Render(map[string]string{
- "project": project,
- })
- if err != nil {
- panic(err)
- }
- return path
-}
-
-// InstanceAdminInstanceConfigPath returns the path for the instance config resource.
-func InstanceAdminInstanceConfigPath(project, instanceConfig string) string {
- path, err := instanceAdminInstanceConfigPathTemplate.Render(map[string]string{
- "project": project,
- "instance_config": instanceConfig,
- })
- if err != nil {
- panic(err)
- }
- return path
-}
-
-// InstanceAdminInstancePath returns the path for the instance resource.
-func InstanceAdminInstancePath(project, instance string) string {
- path, err := instanceAdminInstancePathTemplate.Render(map[string]string{
- "project": project,
- "instance": instance,
- })
- if err != nil {
- panic(err)
- }
- return path
-}
-
-// ListInstanceConfigs lists the supported instance configurations for a given project.
-func (c *InstanceAdminClient) ListInstanceConfigs(ctx context.Context, req *instancepb.ListInstanceConfigsRequest, opts ...gax.CallOption) *InstanceConfigIterator {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.ListInstanceConfigs[0:len(c.CallOptions.ListInstanceConfigs):len(c.CallOptions.ListInstanceConfigs)], opts...)
- it := &InstanceConfigIterator{}
- it.InternalFetch = func(pageSize int, pageToken string) ([]*instancepb.InstanceConfig, string, error) {
- var resp *instancepb.ListInstanceConfigsResponse
- req.PageToken = pageToken
- if pageSize > math.MaxInt32 {
- req.PageSize = math.MaxInt32
- } else {
- req.PageSize = int32(pageSize)
- }
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.ListInstanceConfigs(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, "", err
- }
- return resp.InstanceConfigs, resp.NextPageToken, nil
- }
- fetch := func(pageSize int, pageToken string) (string, error) {
- items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
- if err != nil {
- return "", err
- }
- it.items = append(it.items, items...)
- return nextPageToken, nil
- }
- it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
- return it
-}
-
-// GetInstanceConfig gets information about a particular instance configuration.
-func (c *InstanceAdminClient) GetInstanceConfig(ctx context.Context, req *instancepb.GetInstanceConfigRequest, opts ...gax.CallOption) (*instancepb.InstanceConfig, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.GetInstanceConfig[0:len(c.CallOptions.GetInstanceConfig):len(c.CallOptions.GetInstanceConfig)], opts...)
- var resp *instancepb.InstanceConfig
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.GetInstanceConfig(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// ListInstances lists all instances in the given project.
-func (c *InstanceAdminClient) ListInstances(ctx context.Context, req *instancepb.ListInstancesRequest, opts ...gax.CallOption) *InstanceIterator {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.ListInstances[0:len(c.CallOptions.ListInstances):len(c.CallOptions.ListInstances)], opts...)
- it := &InstanceIterator{}
- it.InternalFetch = func(pageSize int, pageToken string) ([]*instancepb.Instance, string, error) {
- var resp *instancepb.ListInstancesResponse
- req.PageToken = pageToken
- if pageSize > math.MaxInt32 {
- req.PageSize = math.MaxInt32
- } else {
- req.PageSize = int32(pageSize)
- }
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.ListInstances(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, "", err
- }
- return resp.Instances, resp.NextPageToken, nil
- }
- fetch := func(pageSize int, pageToken string) (string, error) {
- items, nextPageToken, err := it.InternalFetch(pageSize, pageToken)
- if err != nil {
- return "", err
- }
- it.items = append(it.items, items...)
- return nextPageToken, nil
- }
- it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf)
- return it
-}
-
-// GetInstance gets information about a particular instance.
-func (c *InstanceAdminClient) GetInstance(ctx context.Context, req *instancepb.GetInstanceRequest, opts ...gax.CallOption) (*instancepb.Instance, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.GetInstance[0:len(c.CallOptions.GetInstance):len(c.CallOptions.GetInstance)], opts...)
- var resp *instancepb.Instance
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.GetInstance(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// CreateInstance creates an instance and begins preparing it to begin serving. The
-// returned [long-running operation][google.longrunning.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][google.longrunning.Operation] will
-// have a name of the format `<instance_name>/operations/<operation_id>` and
-// can be used to track creation of the instance. The
-// [metadata][google.longrunning.Operation.metadata] field type is
-// [CreateInstanceMetadata][google.spanner.admin.instance.v1.CreateInstanceMetadata].
-// The [response][google.longrunning.Operation.response] field type is
-// [Instance][google.spanner.admin.instance.v1.Instance], if successful.
-func (c *InstanceAdminClient) CreateInstance(ctx context.Context, req *instancepb.CreateInstanceRequest, opts ...gax.CallOption) (*CreateInstanceOperation, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.CreateInstance[0:len(c.CallOptions.CreateInstance):len(c.CallOptions.CreateInstance)], opts...)
- var resp *longrunningpb.Operation
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.CreateInstance(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return &CreateInstanceOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, resp),
- }, nil
-}
-
-// UpdateInstance updates an instance, and begins allocating or releasing resources
-// as requested. The returned [long-running
-// operation][google.longrunning.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][google.longrunning.Operation] will
-// have a name of the format `<instance_name>/operations/<operation_id>` and
-// can be used to track the instance modification. The
-// [metadata][google.longrunning.Operation.metadata] field type is
-// [UpdateInstanceMetadata][google.spanner.admin.instance.v1.UpdateInstanceMetadata].
-// The [response][google.longrunning.Operation.response] field type is
-// [Instance][google.spanner.admin.instance.v1.Instance], if successful.
-//
-// Authorization requires `spanner.instances.update` permission on
-// resource [name][google.spanner.admin.instance.v1.Instance.name].
-func (c *InstanceAdminClient) UpdateInstance(ctx context.Context, req *instancepb.UpdateInstanceRequest, opts ...gax.CallOption) (*UpdateInstanceOperation, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.UpdateInstance[0:len(c.CallOptions.UpdateInstance):len(c.CallOptions.UpdateInstance)], opts...)
- var resp *longrunningpb.Operation
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.UpdateInstance(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return &UpdateInstanceOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, resp),
- }, nil
-}
-
-// DeleteInstance 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.
-func (c *InstanceAdminClient) DeleteInstance(ctx context.Context, req *instancepb.DeleteInstanceRequest, opts ...gax.CallOption) error {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.DeleteInstance[0:len(c.CallOptions.DeleteInstance):len(c.CallOptions.DeleteInstance)], opts...)
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- _, err = c.instanceAdminClient.DeleteInstance(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- return err
-}
-
-// SetIamPolicy 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].
-func (c *InstanceAdminClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.SetIamPolicy[0:len(c.CallOptions.SetIamPolicy):len(c.CallOptions.SetIamPolicy)], opts...)
- var resp *iampb.Policy
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.SetIamPolicy(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// GetIamPolicy 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].
-func (c *InstanceAdminClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.GetIamPolicy[0:len(c.CallOptions.GetIamPolicy):len(c.CallOptions.GetIamPolicy)], opts...)
- var resp *iampb.Policy
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.GetIamPolicy(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// TestIamPermissions 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.
-func (c *InstanceAdminClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) {
- ctx = insertXGoog(ctx, c.xGoogHeader)
- opts = append(c.CallOptions.TestIamPermissions[0:len(c.CallOptions.TestIamPermissions):len(c.CallOptions.TestIamPermissions)], opts...)
- var resp *iampb.TestIamPermissionsResponse
- err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
- var err error
- resp, err = c.instanceAdminClient.TestIamPermissions(ctx, req, settings.GRPC...)
- return err
- }, opts...)
- if err != nil {
- return nil, err
- }
- return resp, nil
-}
-
-// InstanceConfigIterator manages a stream of *instancepb.InstanceConfig.
-type InstanceConfigIterator struct {
- items []*instancepb.InstanceConfig
- pageInfo *iterator.PageInfo
- nextFunc func() error
-
- // InternalFetch is for use by the Google Cloud Libraries only.
- // It is not part of the stable interface of this package.
- //
- // InternalFetch returns results from a single call to the underlying RPC.
- // The number of results is no greater than pageSize.
- // If there are no more results, nextPageToken is empty and err is nil.
- InternalFetch func(pageSize int, pageToken string) (results []*instancepb.InstanceConfig, nextPageToken string, err error)
-}
-
-// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
-func (it *InstanceConfigIterator) PageInfo() *iterator.PageInfo {
- return it.pageInfo
-}
-
-// Next returns the next result. Its second return value is iterator.Done if there are no more
-// results. Once Next returns Done, all subsequent calls will return Done.
-func (it *InstanceConfigIterator) Next() (*instancepb.InstanceConfig, error) {
- var item *instancepb.InstanceConfig
- if err := it.nextFunc(); err != nil {
- return item, err
- }
- item = it.items[0]
- it.items = it.items[1:]
- return item, nil
-}
-
-func (it *InstanceConfigIterator) bufLen() int {
- return len(it.items)
-}
-
-func (it *InstanceConfigIterator) takeBuf() interface{} {
- b := it.items
- it.items = nil
- return b
-}
-
-// InstanceIterator manages a stream of *instancepb.Instance.
-type InstanceIterator struct {
- items []*instancepb.Instance
- pageInfo *iterator.PageInfo
- nextFunc func() error
-
- // InternalFetch is for use by the Google Cloud Libraries only.
- // It is not part of the stable interface of this package.
- //
- // InternalFetch returns results from a single call to the underlying RPC.
- // The number of results is no greater than pageSize.
- // If there are no more results, nextPageToken is empty and err is nil.
- InternalFetch func(pageSize int, pageToken string) (results []*instancepb.Instance, nextPageToken string, err error)
-}
-
-// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
-func (it *InstanceIterator) PageInfo() *iterator.PageInfo {
- return it.pageInfo
-}
-
-// Next returns the next result. Its second return value is iterator.Done if there are no more
-// results. Once Next returns Done, all subsequent calls will return Done.
-func (it *InstanceIterator) Next() (*instancepb.Instance, error) {
- var item *instancepb.Instance
- if err := it.nextFunc(); err != nil {
- return item, err
- }
- item = it.items[0]
- it.items = it.items[1:]
- return item, nil
-}
-
-func (it *InstanceIterator) bufLen() int {
- return len(it.items)
-}
-
-func (it *InstanceIterator) takeBuf() interface{} {
- b := it.items
- it.items = nil
- return b
-}
-
-// CreateInstanceOperation manages a long-running operation from CreateInstance.
-type CreateInstanceOperation struct {
- lro *longrunning.Operation
-}
-
-// CreateInstanceOperation returns a new CreateInstanceOperation from a given name.
-// The name must be that of a previously created CreateInstanceOperation, possibly from a different process.
-func (c *InstanceAdminClient) CreateInstanceOperation(name string) *CreateInstanceOperation {
- return &CreateInstanceOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}),
- }
-}
-
-// Wait blocks until the long-running operation is completed, returning the response and any errors encountered.
-//
-// See documentation of Poll for error-handling information.
-func (op *CreateInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) {
- var resp instancepb.Instance
- if err := op.lro.Wait(ctx, &resp, opts...); err != nil {
- return nil, err
- }
- return &resp, nil
-}
-
-// Poll fetches the latest state of the long-running operation.
-//
-// Poll also fetches the latest metadata, which can be retrieved by Metadata.
-//
-// If Poll fails, the error is returned and op is unmodified. If Poll succeeds and
-// the operation has completed with failure, the error is returned and op.Done will return true.
-// If Poll succeeds and the operation has completed successfully,
-// op.Done will return true, and the response of the operation is returned.
-// If Poll succeeds and the operation has not completed, the returned response and error are both nil.
-func (op *CreateInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) {
- var resp instancepb.Instance
- if err := op.lro.Poll(ctx, &resp, opts...); err != nil {
- return nil, err
- }
- if !op.Done() {
- return nil, nil
- }
- return &resp, nil
-}
-
-// Metadata returns metadata associated with the long-running operation.
-// Metadata itself does not contact the server, but Poll does.
-// To get the latest metadata, call this method after a successful call to Poll.
-// If the metadata is not available, the returned metadata and error are both nil.
-func (op *CreateInstanceOperation) Metadata() (*instancepb.CreateInstanceMetadata, error) {
- var meta instancepb.CreateInstanceMetadata
- if err := op.lro.Metadata(&meta); err == longrunning.ErrNoMetadata {
- return nil, nil
- } else if err != nil {
- return nil, err
- }
- return &meta, nil
-}
-
-// Done reports whether the long-running operation has completed.
-func (op *CreateInstanceOperation) Done() bool {
- return op.lro.Done()
-}
-
-// Name returns the name of the long-running operation.
-// The name is assigned by the server and is unique within the service from which the operation is created.
-func (op *CreateInstanceOperation) Name() string {
- return op.lro.Name()
-}
-
-// UpdateInstanceOperation manages a long-running operation from UpdateInstance.
-type UpdateInstanceOperation struct {
- lro *longrunning.Operation
-}
-
-// UpdateInstanceOperation returns a new UpdateInstanceOperation from a given name.
-// The name must be that of a previously created UpdateInstanceOperation, possibly from a different process.
-func (c *InstanceAdminClient) UpdateInstanceOperation(name string) *UpdateInstanceOperation {
- return &UpdateInstanceOperation{
- lro: longrunning.InternalNewOperation(c.LROClient, &longrunningpb.Operation{Name: name}),
- }
-}
-
-// Wait blocks until the long-running operation is completed, returning the response and any errors encountered.
-//
-// See documentation of Poll for error-handling information.
-func (op *UpdateInstanceOperation) Wait(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) {
- var resp instancepb.Instance
- if err := op.lro.Wait(ctx, &resp, opts...); err != nil {
- return nil, err
- }
- return &resp, nil
-}
-
-// Poll fetches the latest state of the long-running operation.
-//
-// Poll also fetches the latest metadata, which can be retrieved by Metadata.
-//
-// If Poll fails, the error is returned and op is unmodified. If Poll succeeds and
-// the operation has completed with failure, the error is returned and op.Done will return true.
-// If Poll succeeds and the operation has completed successfully,
-// op.Done will return true, and the response of the operation is returned.
-// If Poll succeeds and the operation has not completed, the returned response and error are both nil.
-func (op *UpdateInstanceOperation) Poll(ctx context.Context, opts ...gax.CallOption) (*instancepb.Instance, error) {
- var resp instancepb.Instance
- if err := op.lro.Poll(ctx, &resp, opts...); err != nil {
- return nil, err
- }
- if !op.Done() {
- return nil, nil
- }
- return &resp, nil
-}
-
-// Metadata returns metadata associated with the long-running operation.
-// Metadata itself does not contact the server, but Poll does.
-// To get the latest metadata, call this method after a successful call to Poll.
-// If the metadata is not available, the returned metadata and error are both nil.
-func (op *UpdateInstanceOperation) Metadata() (*instancepb.UpdateInstanceMetadata, error) {
- var meta instancepb.UpdateInstanceMetadata
- if err := op.lro.Metadata(&meta); err == longrunning.ErrNoMetadata {
- return nil, nil
- } else if err != nil {
- return nil, err
- }
- return &meta, nil
-}
-
-// Done reports whether the long-running operation has completed.
-func (op *UpdateInstanceOperation) Done() bool {
- return op.lro.Done()
-}
-
-// Name returns the name of the long-running operation.
-// The name is assigned by the server and is unique within the service from which the operation is created.
-func (op *UpdateInstanceOperation) Name() string {
- return op.lro.Name()
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go
deleted file mode 100644
index ee807fdbc..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/instance_admin_client_example_test.go
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-package instance_test
-
-import (
- "cloud.google.com/go/spanner/admin/instance/apiv1"
- "golang.org/x/net/context"
- iampb "google.golang.org/genproto/googleapis/iam/v1"
- instancepb "google.golang.org/genproto/googleapis/spanner/admin/instance/v1"
-)
-
-func ExampleNewInstanceAdminClient() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use client.
- _ = c
-}
-
-func ExampleInstanceAdminClient_ListInstanceConfigs() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.ListInstanceConfigsRequest{
- // TODO: Fill request struct fields.
- }
- it := c.ListInstanceConfigs(ctx, req)
- for {
- resp, err := it.Next()
- if err != nil {
- // TODO: Handle error.
- break
- }
- // TODO: Use resp.
- _ = resp
- }
-}
-
-func ExampleInstanceAdminClient_GetInstanceConfig() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.GetInstanceConfigRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.GetInstanceConfig(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleInstanceAdminClient_ListInstances() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.ListInstancesRequest{
- // TODO: Fill request struct fields.
- }
- it := c.ListInstances(ctx, req)
- for {
- resp, err := it.Next()
- if err != nil {
- // TODO: Handle error.
- break
- }
- // TODO: Use resp.
- _ = resp
- }
-}
-
-func ExampleInstanceAdminClient_GetInstance() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.GetInstanceRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.GetInstance(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleInstanceAdminClient_CreateInstance() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.CreateInstanceRequest{
- // TODO: Fill request struct fields.
- }
- op, err := c.CreateInstance(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
-
- resp, err := op.Wait(ctx)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleInstanceAdminClient_UpdateInstance() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.UpdateInstanceRequest{
- // TODO: Fill request struct fields.
- }
- op, err := c.UpdateInstance(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
-
- resp, err := op.Wait(ctx)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleInstanceAdminClient_DeleteInstance() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &instancepb.DeleteInstanceRequest{
- // TODO: Fill request struct fields.
- }
- err = c.DeleteInstance(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-func ExampleInstanceAdminClient_SetIamPolicy() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &iampb.SetIamPolicyRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.SetIamPolicy(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleInstanceAdminClient_GetIamPolicy() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &iampb.GetIamPolicyRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.GetIamPolicy(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
-
-func ExampleInstanceAdminClient_TestIamPermissions() {
- ctx := context.Background()
- c, err := instance.NewInstanceAdminClient(ctx)
- if err != nil {
- // TODO: Handle error.
- }
-
- req := &iampb.TestIamPermissionsRequest{
- // TODO: Fill request struct fields.
- }
- resp, err := c.TestIamPermissions(ctx, req)
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: Use resp.
- _ = resp
-}
diff --git a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go b/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go
deleted file mode 100644
index 8728d0b25..000000000
--- a/vendor/cloud.google.com/go/spanner/admin/instance/apiv1/mock_test.go
+++ /dev/null
@@ -1,896 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-//
-// 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.
-
-// AUTO-GENERATED CODE. DO NOT EDIT.
-
-package instance
-
-import (
- emptypb "github.com/golang/protobuf/ptypes/empty"
- iampb "google.golang.org/genproto/googleapis/iam/v1"
- longrunningpb "google.golang.org/genproto/googleapis/longrunning"
- instancepb "google.golang.org/genproto/googleapis/spanner/admin/instance/v1"
- field_maskpb "google.golang.org/genproto/protobuf/field_mask"
-)
-
-import (
- "flag"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "strings"
- "testing"
-
- "github.com/golang/protobuf/proto"
- "github.com/golang/protobuf/ptypes"
- "golang.org/x/net/context"
- "google.golang.org/api/option"
- status "google.golang.org/genproto/googleapis/rpc/status"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-var _ = io.EOF
-var _ = ptypes.MarshalAny
-var _ status.Status
-
-type mockInstanceAdminServer struct {
- // Embed for forward compatibility.
- // Tests will keep working if more methods are added
- // in the future.
- instancepb.InstanceAdminServer
-
- reqs []proto.Message
-
- // If set, all calls return this error.
- err error
-
- // responses to return if err == nil
- resps []proto.Message
-}
-
-func (s *mockInstanceAdminServer) ListInstanceConfigs(ctx context.Context, req *instancepb.ListInstanceConfigsRequest) (*instancepb.ListInstanceConfigsResponse, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*instancepb.ListInstanceConfigsResponse), nil
-}
-
-func (s *mockInstanceAdminServer) GetInstanceConfig(ctx context.Context, req *instancepb.GetInstanceConfigRequest) (*instancepb.InstanceConfig, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*instancepb.InstanceConfig), nil
-}
-
-func (s *mockInstanceAdminServer) ListInstances(ctx context.Context, req *instancepb.ListInstancesRequest) (*instancepb.ListInstancesResponse, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*instancepb.ListInstancesResponse), nil
-}
-
-func (s *mockInstanceAdminServer) GetInstance(ctx context.Context, req *instancepb.GetInstanceRequest) (*instancepb.Instance, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*instancepb.Instance), nil
-}
-
-func (s *mockInstanceAdminServer) CreateInstance(ctx context.Context, req *instancepb.CreateInstanceRequest) (*longrunningpb.Operation, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*longrunningpb.Operation), nil
-}
-
-func (s *mockInstanceAdminServer) UpdateInstance(ctx context.Context, req *instancepb.UpdateInstanceRequest) (*longrunningpb.Operation, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*longrunningpb.Operation), nil
-}
-
-func (s *mockInstanceAdminServer) DeleteInstance(ctx context.Context, req *instancepb.DeleteInstanceRequest) (*emptypb.Empty, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*emptypb.Empty), nil
-}
-
-func (s *mockInstanceAdminServer) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest) (*iampb.Policy, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*iampb.Policy), nil
-}
-
-func (s *mockInstanceAdminServer) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest) (*iampb.Policy, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*iampb.Policy), nil
-}
-
-func (s *mockInstanceAdminServer) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest) (*iampb.TestIamPermissionsResponse, error) {
- md, _ := metadata.FromIncomingContext(ctx)
- if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") {
- return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg)
- }
- s.reqs = append(s.reqs, req)
- if s.err != nil {
- return nil, s.err
- }
- return s.resps[0].(*iampb.TestIamPermissionsResponse), nil
-}
-
-// clientOpt is the option tests should use to connect to the test server.
-// It is initialized by TestMain.
-var clientOpt option.ClientOption
-
-var (
- mockInstanceAdmin mockInstanceAdminServer
-)
-
-func TestMain(m *testing.M) {
- flag.Parse()
-
- serv := grpc.NewServer()
- instancepb.RegisterInstanceAdminServer(serv, &mockInstanceAdmin)
-
- lis, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- log.Fatal(err)
- }
- go serv.Serve(lis)
-
- conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure())
- if err != nil {
- log.Fatal(err)
- }
- clientOpt = option.WithGRPCConn(conn)
-
- os.Exit(m.Run())
-}
-
-func TestInstanceAdminListInstanceConfigs(t *testing.T) {
- var nextPageToken string = ""
- var instanceConfigsElement *instancepb.InstanceConfig = &instancepb.InstanceConfig{}
- var instanceConfigs = []*instancepb.InstanceConfig{instanceConfigsElement}
- var expectedResponse = &instancepb.ListInstanceConfigsResponse{
- NextPageToken: nextPageToken,
- InstanceConfigs: instanceConfigs,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
- var request = &instancepb.ListInstanceConfigsRequest{
- Parent: formattedParent,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.ListInstanceConfigs(context.Background(), request).Next()
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- want := (interface{})(expectedResponse.InstanceConfigs[0])
- got := (interface{})(resp)
- var ok bool
-
- switch want := (want).(type) {
- case proto.Message:
- ok = proto.Equal(want, got.(proto.Message))
- default:
- ok = want == got
- }
- if !ok {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminListInstanceConfigsError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
- var request = &instancepb.ListInstanceConfigsRequest{
- Parent: formattedParent,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.ListInstanceConfigs(context.Background(), request).Next()
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminGetInstanceConfig(t *testing.T) {
- var name2 string = "name2-1052831874"
- var displayName string = "displayName1615086568"
- var expectedResponse = &instancepb.InstanceConfig{
- Name: name2,
- DisplayName: displayName,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedName string = InstanceAdminInstanceConfigPath("[PROJECT]", "[INSTANCE_CONFIG]")
- var request = &instancepb.GetInstanceConfigRequest{
- Name: formattedName,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetInstanceConfig(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminGetInstanceConfigError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedName string = InstanceAdminInstanceConfigPath("[PROJECT]", "[INSTANCE_CONFIG]")
- var request = &instancepb.GetInstanceConfigRequest{
- Name: formattedName,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetInstanceConfig(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminListInstances(t *testing.T) {
- var nextPageToken string = ""
- var instancesElement *instancepb.Instance = &instancepb.Instance{}
- var instances = []*instancepb.Instance{instancesElement}
- var expectedResponse = &instancepb.ListInstancesResponse{
- NextPageToken: nextPageToken,
- Instances: instances,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
- var request = &instancepb.ListInstancesRequest{
- Parent: formattedParent,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.ListInstances(context.Background(), request).Next()
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- want := (interface{})(expectedResponse.Instances[0])
- got := (interface{})(resp)
- var ok bool
-
- switch want := (want).(type) {
- case proto.Message:
- ok = proto.Equal(want, got.(proto.Message))
- default:
- ok = want == got
- }
- if !ok {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminListInstancesError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
- var request = &instancepb.ListInstancesRequest{
- Parent: formattedParent,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.ListInstances(context.Background(), request).Next()
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminGetInstance(t *testing.T) {
- var name2 string = "name2-1052831874"
- var config string = "config-1354792126"
- var displayName string = "displayName1615086568"
- var nodeCount int32 = 1539922066
- var expectedResponse = &instancepb.Instance{
- Name: name2,
- Config: config,
- DisplayName: displayName,
- NodeCount: nodeCount,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &instancepb.GetInstanceRequest{
- Name: formattedName,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetInstance(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminGetInstanceError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &instancepb.GetInstanceRequest{
- Name: formattedName,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetInstance(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminCreateInstance(t *testing.T) {
- var name string = "name3373707"
- var config string = "config-1354792126"
- var displayName string = "displayName1615086568"
- var nodeCount int32 = 1539922066
- var expectedResponse = &instancepb.Instance{
- Name: name,
- Config: config,
- DisplayName: displayName,
- NodeCount: nodeCount,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- any, err := ptypes.MarshalAny(expectedResponse)
- if err != nil {
- t.Fatal(err)
- }
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Response{Response: any},
- })
-
- var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
- var instanceId string = "instanceId-2101995259"
- var instance *instancepb.Instance = &instancepb.Instance{}
- var request = &instancepb.CreateInstanceRequest{
- Parent: formattedParent,
- InstanceId: instanceId,
- Instance: instance,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.CreateInstance(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- resp, err := respLRO.Wait(context.Background())
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminCreateInstanceError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Error{
- Error: &status.Status{
- Code: int32(errCode),
- Message: "test error",
- },
- },
- })
-
- var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
- var instanceId string = "instanceId-2101995259"
- var instance *instancepb.Instance = &instancepb.Instance{}
- var request = &instancepb.CreateInstanceRequest{
- Parent: formattedParent,
- InstanceId: instanceId,
- Instance: instance,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.CreateInstance(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- resp, err := respLRO.Wait(context.Background())
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminUpdateInstance(t *testing.T) {
- var name string = "name3373707"
- var config string = "config-1354792126"
- var displayName string = "displayName1615086568"
- var nodeCount int32 = 1539922066
- var expectedResponse = &instancepb.Instance{
- Name: name,
- Config: config,
- DisplayName: displayName,
- NodeCount: nodeCount,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- any, err := ptypes.MarshalAny(expectedResponse)
- if err != nil {
- t.Fatal(err)
- }
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Response{Response: any},
- })
-
- var instance *instancepb.Instance = &instancepb.Instance{}
- var fieldMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{}
- var request = &instancepb.UpdateInstanceRequest{
- Instance: instance,
- FieldMask: fieldMask,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.UpdateInstance(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- resp, err := respLRO.Wait(context.Background())
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminUpdateInstanceError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], &longrunningpb.Operation{
- Name: "longrunning-test",
- Done: true,
- Result: &longrunningpb.Operation_Error{
- Error: &status.Status{
- Code: int32(errCode),
- Message: "test error",
- },
- },
- })
-
- var instance *instancepb.Instance = &instancepb.Instance{}
- var fieldMask *field_maskpb.FieldMask = &field_maskpb.FieldMask{}
- var request = &instancepb.UpdateInstanceRequest{
- Instance: instance,
- FieldMask: fieldMask,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- respLRO, err := c.UpdateInstance(context.Background(), request)
- if err != nil {
- t.Fatal(err)
- }
- resp, err := respLRO.Wait(context.Background())
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminDeleteInstance(t *testing.T) {
- var expectedResponse *emptypb.Empty = &emptypb.Empty{}
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &instancepb.DeleteInstanceRequest{
- Name: formattedName,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- err = c.DeleteInstance(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
-}
-
-func TestInstanceAdminDeleteInstanceError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &instancepb.DeleteInstanceRequest{
- Name: formattedName,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- err = c.DeleteInstance(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
-}
-func TestInstanceAdminSetIamPolicy(t *testing.T) {
- var version int32 = 351608024
- var etag []byte = []byte("21")
- var expectedResponse = &iampb.Policy{
- Version: version,
- Etag: etag,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var policy *iampb.Policy = &iampb.Policy{}
- var request = &iampb.SetIamPolicyRequest{
- Resource: formattedResource,
- Policy: policy,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.SetIamPolicy(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminSetIamPolicyError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var policy *iampb.Policy = &iampb.Policy{}
- var request = &iampb.SetIamPolicyRequest{
- Resource: formattedResource,
- Policy: policy,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.SetIamPolicy(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminGetIamPolicy(t *testing.T) {
- var version int32 = 351608024
- var etag []byte = []byte("21")
- var expectedResponse = &iampb.Policy{
- Version: version,
- Etag: etag,
- }
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &iampb.GetIamPolicyRequest{
- Resource: formattedResource,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetIamPolicy(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminGetIamPolicyError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var request = &iampb.GetIamPolicyRequest{
- Resource: formattedResource,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.GetIamPolicy(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
-func TestInstanceAdminTestIamPermissions(t *testing.T) {
- var expectedResponse *iampb.TestIamPermissionsResponse = &iampb.TestIamPermissionsResponse{}
-
- mockInstanceAdmin.err = nil
- mockInstanceAdmin.reqs = nil
-
- mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
-
- var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var permissions []string = nil
- var request = &iampb.TestIamPermissionsRequest{
- Resource: formattedResource,
- Permissions: permissions,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.TestIamPermissions(context.Background(), request)
-
- if err != nil {
- t.Fatal(err)
- }
-
- if want, got := request, mockInstanceAdmin.reqs[0]; !proto.Equal(want, got) {
- t.Errorf("wrong request %q, want %q", got, want)
- }
-
- if want, got := expectedResponse, resp; !proto.Equal(want, got) {
- t.Errorf("wrong response %q, want %q)", got, want)
- }
-}
-
-func TestInstanceAdminTestIamPermissionsError(t *testing.T) {
- errCode := codes.PermissionDenied
- mockInstanceAdmin.err = grpc.Errorf(errCode, "test error")
-
- var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
- var permissions []string = nil
- var request = &iampb.TestIamPermissionsRequest{
- Resource: formattedResource,
- Permissions: permissions,
- }
-
- c, err := NewInstanceAdminClient(context.Background(), clientOpt)
- if err != nil {
- t.Fatal(err)
- }
-
- resp, err := c.TestIamPermissions(context.Background(), request)
-
- if c := grpc.Code(err); c != errCode {
- t.Errorf("got error code %q, want %q", c, errCode)
- }
- _ = resp
-}
diff --git a/vendor/cloud.google.com/go/spanner/backoff.go b/vendor/cloud.google.com/go/spanner/backoff.go
deleted file mode 100644
index d38723843..000000000
--- a/vendor/cloud.google.com/go/spanner/backoff.go
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "math/rand"
- "time"
-)
-
-const (
- // minBackoff is the minimum backoff used by default.
- minBackoff = 1 * time.Second
- // maxBackoff is the maximum backoff used by default.
- maxBackoff = 32 * time.Second
- // jitter is the jitter factor.
- jitter = 0.4
- // rate is the rate of exponential increase in the backoff.
- rate = 1.3
-)
-
-var defaultBackoff = exponentialBackoff{minBackoff, maxBackoff}
-
-type exponentialBackoff struct {
- min, max time.Duration
-}
-
-// delay calculates the delay that should happen at n-th
-// exponential backoff in a series.
-func (b exponentialBackoff) delay(retries int) time.Duration {
- min, max := float64(b.min), float64(b.max)
- delay := min
- for delay < max && retries > 0 {
- delay *= rate
- retries--
- }
- if delay > max {
- delay = max
- }
- delay -= delay * jitter * rand.Float64()
- if delay < min {
- delay = min
- }
- return time.Duration(delay)
-}
diff --git a/vendor/cloud.google.com/go/spanner/backoff_test.go b/vendor/cloud.google.com/go/spanner/backoff_test.go
deleted file mode 100644
index 7a0314e81..000000000
--- a/vendor/cloud.google.com/go/spanner/backoff_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "math"
- "time"
-
- "testing"
-)
-
-// Test if exponential backoff helper can produce correct series of
-// retry delays.
-func TestBackoff(t *testing.T) {
- b := exponentialBackoff{minBackoff, maxBackoff}
- tests := []struct {
- retries int
- min time.Duration
- max time.Duration
- }{
- {
- retries: 0,
- min: minBackoff,
- max: minBackoff,
- },
- {
- retries: 1,
- min: minBackoff,
- max: time.Duration(rate * float64(minBackoff)),
- },
- {
- retries: 3,
- min: time.Duration(math.Pow(rate, 3) * (1 - jitter) * float64(minBackoff)),
- max: time.Duration(math.Pow(rate, 3) * float64(minBackoff)),
- },
- {
- retries: 1000,
- min: time.Duration((1 - jitter) * float64(maxBackoff)),
- max: maxBackoff,
- },
- }
- for _, test := range tests {
- got := b.delay(test.retries)
- if float64(got) < float64(test.min) || float64(got) > float64(test.max) {
- t.Errorf("delay(%v) = %v, want in range [%v, %v]", test.retries, got, test.min, test.max)
- }
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/client.go b/vendor/cloud.google.com/go/spanner/client.go
deleted file mode 100644
index 4ccc9333a..000000000
--- a/vendor/cloud.google.com/go/spanner/client.go
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
- "regexp"
- "sync/atomic"
- "time"
-
- "cloud.google.com/go/internal/version"
- "golang.org/x/net/context"
- "google.golang.org/api/option"
- "google.golang.org/api/transport"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-const (
- prodAddr = "spanner.googleapis.com:443"
-
- // resourcePrefixHeader is the name of the metadata header used to indicate
- // the resource being operated on.
- resourcePrefixHeader = "google-cloud-resource-prefix"
- // apiClientHeader is the name of the metadata header used to indicate client
- // information.
- apiClientHeader = "x-goog-api-client"
-
- // numChannels is the default value for NumChannels of client
- numChannels = 4
-)
-
-const (
- // Scope is the scope for Cloud Spanner Data API.
- Scope = "https://www.googleapis.com/auth/spanner.data"
-
- // AdminScope is the scope for Cloud Spanner Admin APIs.
- AdminScope = "https://www.googleapis.com/auth/spanner.admin"
-)
-
-var (
- validDBPattern = regexp.MustCompile("^projects/[^/]+/instances/[^/]+/databases/[^/]+$")
- clientUserAgent = fmt.Sprintf("gl-go/%s gccl/%s grpc/%s", version.Go(), version.Repo, grpc.Version)
-)
-
-func validDatabaseName(db string) error {
- if matched := validDBPattern.MatchString(db); !matched {
- return fmt.Errorf("database name %q should conform to pattern %q",
- db, validDBPattern.String())
- }
- return nil
-}
-
-// Client is a client for reading and writing data to a Cloud Spanner database. A
-// client is safe to use concurrently, except for its Close method.
-type Client struct {
- // rr must be accessed through atomic operations.
- rr uint32
- conns []*grpc.ClientConn
- clients []sppb.SpannerClient
- database string
- // Metadata to be sent with each request.
- md metadata.MD
- idleSessions *sessionPool
-}
-
-// ClientConfig has configurations for the client.
-type ClientConfig struct {
- // NumChannels is the number of GRPC channels.
- // If zero, numChannels is used.
- NumChannels int
- co []option.ClientOption
- // SessionPoolConfig is the configuration for session pool.
- SessionPoolConfig
-}
-
-// errDial returns error for dialing to Cloud Spanner.
-func errDial(ci int, err error) error {
- e := toSpannerError(err).(*Error)
- e.decorate(fmt.Sprintf("dialing fails for channel[%v]", ci))
- return e
-}
-
-func contextWithOutgoingMetadata(ctx context.Context, md metadata.MD) context.Context {
- existing, ok := metadata.FromOutgoingContext(ctx)
- if ok {
- md = metadata.Join(existing, md)
- }
- return metadata.NewOutgoingContext(ctx, md)
-}
-
-// NewClient creates a client to a database. A valid database name has the
-// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID. It uses a default
-// configuration.
-func NewClient(ctx context.Context, database string, opts ...option.ClientOption) (*Client, error) {
- return NewClientWithConfig(ctx, database, ClientConfig{}, opts...)
-}
-
-// NewClientWithConfig creates a client to a database. A valid database name has the
-// form projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_ID.
-func NewClientWithConfig(ctx context.Context, database string, config ClientConfig, opts ...option.ClientOption) (*Client, error) {
- // Validate database path.
- if err := validDatabaseName(database); err != nil {
- return nil, err
- }
- c := &Client{
- database: database,
- md: metadata.Pairs(
- resourcePrefixHeader, database,
- apiClientHeader, clientUserAgent),
- }
- allOpts := []option.ClientOption{option.WithEndpoint(prodAddr), option.WithScopes(Scope), option.WithUserAgent(clientUserAgent), option.WithGRPCDialOption(grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20)))}
- allOpts = append(allOpts, opts...)
- // Prepare gRPC channels.
- if config.NumChannels == 0 {
- config.NumChannels = numChannels
- }
- // Default MaxOpened sessions
- if config.MaxOpened == 0 {
- config.MaxOpened = uint64(config.NumChannels * 100)
- }
- if config.MaxBurst == 0 {
- config.MaxBurst = 10
- }
- for i := 0; i < config.NumChannels; i++ {
- conn, err := transport.DialGRPC(ctx, allOpts...)
- if err != nil {
- return nil, errDial(i, err)
- }
- c.conns = append(c.conns, conn)
- c.clients = append(c.clients, sppb.NewSpannerClient(conn))
- }
- // Prepare session pool.
- config.SessionPoolConfig.getRPCClient = func() (sppb.SpannerClient, error) {
- // TODO: support more loadbalancing options.
- return c.rrNext(), nil
- }
- sp, err := newSessionPool(database, config.SessionPoolConfig, c.md)
- if err != nil {
- c.Close()
- return nil, err
- }
- c.idleSessions = sp
- return c, nil
-}
-
-// rrNext returns the next available Cloud Spanner RPC client in a round-robin manner.
-func (c *Client) rrNext() sppb.SpannerClient {
- return c.clients[atomic.AddUint32(&c.rr, 1)%uint32(len(c.clients))]
-}
-
-// Close closes the client.
-func (c *Client) Close() {
- if c.idleSessions != nil {
- c.idleSessions.close()
- }
- for _, conn := range c.conns {
- conn.Close()
- }
-}
-
-// Single provides a read-only snapshot transaction optimized for the case
-// where only a single read or query is needed. This is more efficient than
-// using ReadOnlyTransaction() for a single read or query.
-//
-// Single will use a strong TimestampBound by default. Use
-// ReadOnlyTransaction.WithTimestampBound to specify a different
-// TimestampBound. A non-strong bound can be used to reduce latency, or
-// "time-travel" to prior versions of the database, see the documentation of
-// TimestampBound for details.
-func (c *Client) Single() *ReadOnlyTransaction {
- t := &ReadOnlyTransaction{singleUse: true, sp: c.idleSessions}
- t.txReadOnly.txReadEnv = t
- return t
-}
-
-// ReadOnlyTransaction returns a ReadOnlyTransaction that can be used for
-// multiple reads from the database. You must call Close() when the
-// ReadOnlyTransaction is no longer needed to release resources on the server.
-//
-// ReadOnlyTransaction will use a strong TimestampBound by default. Use
-// ReadOnlyTransaction.WithTimestampBound to specify a different
-// TimestampBound. A non-strong bound can be used to reduce latency, or
-// "time-travel" to prior versions of the database, see the documentation of
-// TimestampBound for details.
-func (c *Client) ReadOnlyTransaction() *ReadOnlyTransaction {
- t := &ReadOnlyTransaction{
- singleUse: false,
- sp: c.idleSessions,
- txReadyOrClosed: make(chan struct{}),
- }
- t.txReadOnly.txReadEnv = t
- return t
-}
-
-type transactionInProgressKey struct{}
-
-func checkNestedTxn(ctx context.Context) error {
- if ctx.Value(transactionInProgressKey{}) != nil {
- return spannerErrorf(codes.FailedPrecondition, "Cloud Spanner does not support nested transactions")
- }
- return nil
-}
-
-// ReadWriteTransaction executes a read-write transaction, with retries as
-// necessary.
-//
-// The function f will be called one or more times. It must not maintain
-// any state between calls.
-//
-// If the transaction cannot be committed or if f returns an IsAborted error,
-// ReadWriteTransaction will call f again. It will continue to call f until the
-// transaction can be committed or the Context times out or is cancelled. If f
-// returns an error other than IsAborted, ReadWriteTransaction will abort the
-// transaction and return the error.
-//
-// To limit the number of retries, set a deadline on the Context rather than
-// using a fixed limit on the number of attempts. ReadWriteTransaction will
-// retry as needed until that deadline is met.
-func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error) (time.Time, error) {
- if err := checkNestedTxn(ctx); err != nil {
- return time.Time{}, err
- }
- var (
- ts time.Time
- sh *sessionHandle
- )
- err := runRetryable(ctx, func(ctx context.Context) error {
- var (
- err error
- t *ReadWriteTransaction
- )
- if sh == nil || sh.getID() == "" || sh.getClient() == nil {
- // Session handle hasn't been allocated or has been destroyed.
- sh, err = c.idleSessions.takeWriteSession(ctx)
- if err != nil {
- // If session retrieval fails, just fail the transaction.
- return err
- }
- t = &ReadWriteTransaction{
- sh: sh,
- tx: sh.getTransactionID(),
- }
- } else {
- t = &ReadWriteTransaction{
- sh: sh,
- }
- }
- t.txReadOnly.txReadEnv = t
- if err = t.begin(ctx); err != nil {
- // Mask error from begin operation as retryable error.
- return errRetry(err)
- }
- ts, err = t.runInTransaction(ctx, f)
- if err != nil {
- return err
- }
- return nil
- })
- if sh != nil {
- sh.recycle()
- }
- return ts, err
-}
-
-// applyOption controls the behavior of Client.Apply.
-type applyOption struct {
- // If atLeastOnce == true, Client.Apply will execute the mutations on Cloud Spanner at least once.
- atLeastOnce bool
-}
-
-// An ApplyOption is an optional argument to Apply.
-type ApplyOption func(*applyOption)
-
-// ApplyAtLeastOnce returns an ApplyOption that removes replay protection.
-//
-// With this option, Apply may attempt to apply mutations more than once; if
-// the mutations are not idempotent, this may lead to a failure being reported
-// when the mutation was applied more than once. For example, an insert may
-// fail with ALREADY_EXISTS even though the row did not exist before Apply was
-// called. For this reason, most users of the library will prefer not to use
-// this option. However, ApplyAtLeastOnce requires only a single RPC, whereas
-// Apply's default replay protection may require an additional RPC. So this
-// option may be appropriate for latency sensitive and/or high throughput blind
-// writing.
-func ApplyAtLeastOnce() ApplyOption {
- return func(ao *applyOption) {
- ao.atLeastOnce = true
- }
-}
-
-// Apply applies a list of mutations atomically to the database.
-func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption) (time.Time, error) {
- ao := &applyOption{}
- for _, opt := range opts {
- opt(ao)
- }
- if !ao.atLeastOnce {
- return c.ReadWriteTransaction(ctx, func(ctx context.Context, t *ReadWriteTransaction) error {
- t.BufferWrite(ms)
- return nil
- })
- }
- t := &writeOnlyTransaction{c.idleSessions}
- return t.applyAtLeastOnce(ctx, ms...)
-}
diff --git a/vendor/cloud.google.com/go/spanner/client_test.go b/vendor/cloud.google.com/go/spanner/client_test.go
deleted file mode 100644
index 643f863b7..000000000
--- a/vendor/cloud.google.com/go/spanner/client_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "strings"
- "testing"
-)
-
-// Test validDatabaseName()
-func TestValidDatabaseName(t *testing.T) {
- validDbUri := "projects/spanner-cloud-test/instances/foo/databases/foodb"
- invalidDbUris := []string{
- // Completely wrong DB URI.
- "foobarDB",
- // Project ID contains "/".
- "projects/spanner-cloud/test/instances/foo/databases/foodb",
- // No instance ID.
- "projects/spanner-cloud-test/instances//databases/foodb",
- }
- if err := validDatabaseName(validDbUri); err != nil {
- t.Errorf("validateDatabaseName(%q) = %v, want nil", validDbUri, err)
- }
- for _, d := range invalidDbUris {
- if err, wantErr := validDatabaseName(d), "should conform to pattern"; !strings.Contains(err.Error(), wantErr) {
- t.Errorf("validateDatabaseName(%q) = %q, want error pattern %q", validDbUri, err, wantErr)
- }
- }
-}
-
-func TestReadOnlyTransactionClose(t *testing.T) {
- // Closing a ReadOnlyTransaction shouldn't panic.
- c := &Client{}
- tx := c.ReadOnlyTransaction()
- tx.Close()
-}
diff --git a/vendor/cloud.google.com/go/spanner/doc.go b/vendor/cloud.google.com/go/spanner/doc.go
deleted file mode 100644
index 88f0b3221..000000000
--- a/vendor/cloud.google.com/go/spanner/doc.go
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-/*
-Package spanner provides a client for reading and writing to Cloud Spanner
-databases. See the packages under admin for clients that operate on databases
-and instances.
-
-Note: This package is in alpha. Backwards-incompatible changes may occur
-without notice.
-
-See https://cloud.google.com/spanner/docs/getting-started/go/ for an introduction
-to Cloud Spanner and additional help on using this API.
-
-Creating a Client
-
-To start working with this package, create a client that refers to the database
-of interest:
-
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D")
- defer client.Close()
- if err != nil {
- // TODO: Handle error.
- }
-
-Remember to close the client after use to free up the sessions in the session
-pool.
-
-
-Simple Reads and Writes
-
-Two Client methods, Apply and Single, work well for simple reads and writes. As
-a quick introduction, here we write a new row to the database and read it back:
-
- _, err := client.Apply(ctx, []*spanner.Mutation{
- spanner.Insert("Users",
- []string{"name", "email"},
- []interface{}{"alice", "a@example.com"})})
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Users",
- spanner.Key{"alice"}, []string{"email"})
- if err != nil {
- // TODO: Handle error.
- }
-
-All the methods used above are discussed in more detail below.
-
-
-Keys
-
-Every Cloud Spanner row has a unique key, composed of one or more columns.
-Construct keys with a literal of type Key:
-
- key1 := spanner.Key{"alice"}
-
-
-KeyRanges
-
-The keys of a Cloud Spanner table are ordered. You can specify ranges of keys
-using the KeyRange type:
-
- kr1 := spanner.KeyRange{Start: key1, End: key2}
-
-By default, a KeyRange includes its start key but not its end key. Use
-the Kind field to specify other boundary conditions:
-
- // include both keys
- kr2 := spanner.KeyRange{Start: key1, End: key2, Kind: spanner.ClosedClosed}
-
-
-KeySets
-
-A KeySet represents a set of keys. A single Key or KeyRange can act as a KeySet. Use
-the KeySets function to build the union of several KeySets:
-
- ks1 := spanner.KeySets(key1, key2, kr1, kr2)
-
-AllKeys returns a KeySet that refers to all the keys in a table:
-
- ks2 := spanner.AllKeys()
-
-
-Transactions
-
-All Cloud Spanner reads and writes occur inside transactions. There are two
-types of transactions, read-only and read-write. Read-only transactions cannot
-change the database, do not acquire locks, and may access either the current
-database state or states in the past. Read-write transactions can read the
-database before writing to it, and always apply to the most recent database
-state.
-
-
-Single Reads
-
-The simplest and fastest transaction is a ReadOnlyTransaction that supports a
-single read operation. Use Client.Single to create such a transaction. You can
-chain the call to Single with a call to a Read method.
-
-When you only want one row whose key you know, use ReadRow. Provide the table
-name, key, and the columns you want to read:
-
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
-
-Read multiple rows with the Read method. It takes a table name, KeySet, and list
-of columns:
-
- iter := client.Single().Read(ctx, "Accounts", keyset1, columns)
-
-Read returns a RowIterator. You can call the Do method on the iterator and pass
-a callback:
-
- err := iter.Do(func(row *Row) error {
- // TODO: use row
- return nil
- })
-
-RowIterator also follows the standard pattern for the Google
-Cloud Client Libraries:
-
- defer iter.Stop()
- for {
- row, err := iter.Next()
- if err == iterator.Done {
- break
- }
- if err != nil {
- // TODO: Handle error.
- }
- // TODO: use row
- }
-
-Always call Stop when you finish using an iterator this way, whether or not you
-iterate to the end. (Failing to call Stop could lead you to exhaust the
-database's session quota.)
-
-To read rows with an index, use ReadUsingIndex.
-
-Statements
-
-The most general form of reading uses SQL statements. Construct a Statement
-with NewStatement, setting any parameters using the Statement's Params map:
-
- stmt := spanner.NewStatement("SELECT First, Last FROM SINGERS WHERE Last >= @start")
- stmt.Params["start"] = "Dylan"
-
-You can also construct a Statement directly with a struct literal, providing
-your own map of parameters.
-
-Use the Query method to run the statement and obtain an iterator:
-
- iter := client.Single().Query(ctx, stmt)
-
-
-Rows
-
-Once you have a Row, via an iterator or a call to ReadRow, you can extract
-column values in several ways. Pass in a pointer to a Go variable of the
-appropriate type when you extract a value.
-
-You can extract by column position or name:
-
- err := row.Column(0, &name)
- err = row.ColumnByName("balance", &balance)
-
-You can extract all the columns at once:
-
- err = row.Columns(&name, &balance)
-
-Or you can define a Go struct that corresponds to your columns, and extract
-into that:
-
- var s struct { Name string; Balance int64 }
- err = row.ToStruct(&s)
-
-
-For Cloud Spanner columns that may contain NULL, use one of the NullXXX types,
-like NullString:
-
- var ns spanner.NullString
- if err =: row.Column(0, &ns); err != nil {
- // TODO: Handle error.
- }
- if ns.Valid {
- fmt.Println(ns.StringVal)
- } else {
- fmt.Println("column is NULL")
- }
-
-
-Multiple Reads
-
-To perform more than one read in a transaction, use ReadOnlyTransaction:
-
- txn := client.ReadOnlyTransaction()
- defer txn.Close()
- iter := txn.Query(ctx, stmt1)
- // ...
- iter = txn.Query(ctx, stmt2)
- // ...
-
-You must call Close when you are done with the transaction.
-
-
-Timestamps and Timestamp Bounds
-
-Cloud Spanner read-only transactions conceptually perform all their reads at a
-single moment in time, called the transaction's read timestamp. Once a read has
-started, you can call ReadOnlyTransaction's Timestamp method to obtain the read
-timestamp.
-
-By default, a transaction will pick the most recent time (a time where all
-previously committed transactions are visible) for its reads. This provides the
-freshest data, but may involve some delay. You can often get a quicker response
-if you are willing to tolerate "stale" data. You can control the read timestamp
-selected by a transaction by calling the WithTimestampBound method on the
-transaction before using it. For example, to perform a query on data that is at
-most one minute stale, use
-
- client.Single().
- WithTimestampBound(spanner.MaxStaleness(1*time.Minute)).
- Query(ctx, stmt)
-
-See the documentation of TimestampBound for more details.
-
-
-Mutations
-
-To write values to a Cloud Spanner database, construct a Mutation. The spanner
-package has functions for inserting, updating and deleting rows. Except for the
-Delete methods, which take a Key or KeyRange, each mutation-building function
-comes in three varieties.
-
-One takes lists of columns and values along with the table name:
-
- m1 := spanner.Insert("Users",
- []string{"name", "email"},
- []interface{}{"alice", "a@example.com"})
-
-One takes a map from column names to values:
-
- m2 := spanner.InsertMap("Users", map[string]interface{}{
- "name": "alice",
- "email": "a@example.com",
- })
-
-And the third accepts a struct value, and determines the columns from the
-struct field names:
-
- type User struct { Name, Email string }
- u := User{Name: "alice", Email: "a@example.com"}
- m3, err := spanner.InsertStruct("Users", u)
-
-
-Writes
-
-To apply a list of mutations to the database, use Apply:
-
- _, err := client.Apply(ctx, []*spanner.Mutation{m1, m2, m3})
-
-If you need to read before writing in a single transaction, use a
-ReadWriteTransaction. ReadWriteTransactions may abort and need to be retried.
-You pass in a function to ReadWriteTransaction, and the client will handle the
-retries automatically. Use the transaction's BufferWrite method to buffer
-mutations, which will all be executed at the end of the transaction:
-
- _, err := client.ReadWriteTransaction(ctx, func(txn *spanner.ReadWriteTransaction) error {
- var balance int64
- row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
- if err != nil {
- // This function will be called again if this is an IsAborted error.
- return err
- }
- if err := row.Column(0, &balance); err != nil {
- return err
- }
-
- if balance <= 10 {
- return errors.New("insufficient funds in account")
- }
- balance -= 10
- m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance})
- txn.BufferWrite([]*spanner.Mutation{m})
-
- // The buffered mutation will be committed. If the commit
- // fails with an IsAborted error, this function will be called
- // again.
- return nil
- })
-
-Authentication
-
-See examples of authorization and authentication at
-https://godoc.org/cloud.google.com/go#pkg-examples.
-*/
-package spanner // import "cloud.google.com/go/spanner"
diff --git a/vendor/cloud.google.com/go/spanner/errors.go b/vendor/cloud.google.com/go/spanner/errors.go
deleted file mode 100644
index 13106f2ed..000000000
--- a/vendor/cloud.google.com/go/spanner/errors.go
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
-
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-// Error is the structured error returned by Cloud Spanner client.
-type Error struct {
- // Code is the canonical error code for describing the nature of a
- // particular error.
- Code codes.Code
- // Desc explains more details of the error.
- Desc string
- // trailers are the trailers returned in the response, if any.
- trailers metadata.MD
-}
-
-// Error implements error.Error.
-func (e *Error) Error() string {
- if e == nil {
- return fmt.Sprintf("spanner: OK")
- }
- return fmt.Sprintf("spanner: code = %q, desc = %q", e.Code, e.Desc)
-}
-
-// decorate decorates an existing spanner.Error with more information.
-func (e *Error) decorate(info string) {
- e.Desc = fmt.Sprintf("%v, %v", info, e.Desc)
-}
-
-// spannerErrorf generates a *spanner.Error with the given error code and
-// description.
-func spannerErrorf(ec codes.Code, format string, args ...interface{}) error {
- return &Error{
- Code: ec,
- Desc: fmt.Sprintf(format, args...),
- }
-}
-
-// toSpannerError converts general Go error to *spanner.Error.
-func toSpannerError(err error) error {
- return toSpannerErrorWithMetadata(err, nil)
-}
-
-// toSpannerErrorWithMetadata converts general Go error and grpc trailers to *spanner.Error.
-// Note: modifies original error if trailers aren't nil
-func toSpannerErrorWithMetadata(err error, trailers metadata.MD) error {
- if err == nil {
- return nil
- }
- if se, ok := err.(*Error); ok {
- if trailers != nil {
- se.trailers = metadata.Join(se.trailers, trailers)
- }
- return se
- }
- if grpc.Code(err) == codes.Unknown {
- return &Error{codes.Unknown, err.Error(), trailers}
- }
- return &Error{grpc.Code(err), grpc.ErrorDesc(err), trailers}
-}
-
-// ErrCode extracts the canonical error code from a Go error.
-func ErrCode(err error) codes.Code {
- se, ok := toSpannerError(err).(*Error)
- if !ok {
- return codes.Unknown
- }
- return se.Code
-}
-
-// ErrDesc extracts the Cloud Spanner error description from a Go error.
-func ErrDesc(err error) string {
- se, ok := toSpannerError(err).(*Error)
- if !ok {
- return err.Error()
- }
- return se.Desc
-}
-
-// errTrailers extracts the grpc trailers if present from a Go error.
-func errTrailers(err error) metadata.MD {
- se, ok := err.(*Error)
- if !ok {
- return nil
- }
- return se.trailers
-}
diff --git a/vendor/cloud.google.com/go/spanner/examples_test.go b/vendor/cloud.google.com/go/spanner/examples_test.go
deleted file mode 100644
index 4cd19451c..000000000
--- a/vendor/cloud.google.com/go/spanner/examples_test.go
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner_test
-
-import (
- "errors"
- "fmt"
- "time"
-
- "cloud.google.com/go/spanner"
- "golang.org/x/net/context"
- "google.golang.org/api/iterator"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-func ExampleNewClient() {
- ctx := context.Background()
- const myDB = "projects/my-project/instances/my-instance/database/my-db"
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- _ = client // TODO: Use client.
-}
-
-const myDB = "projects/my-project/instances/my-instance/database/my-db"
-
-func ExampleNewClientWithConfig() {
- ctx := context.Background()
- const myDB = "projects/my-project/instances/my-instance/database/my-db"
- client, err := spanner.NewClientWithConfig(ctx, myDB, spanner.ClientConfig{
- NumChannels: 10,
- })
- if err != nil {
- // TODO: Handle error.
- }
- _ = client // TODO: Use client.
- client.Close() // Close client when done.
-}
-
-func ExampleClient_Single() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
- _ = iter // TODO: iterate using Next or Do.
-}
-
-func ExampleClient_ReadOnlyTransaction() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- t := client.ReadOnlyTransaction()
- defer t.Close()
- // TODO: Read with t using Read, ReadRow, ReadUsingIndex, or Query.
-}
-
-func ExampleClient_ReadWriteTransaction() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
- var balance int64
- row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
- if err != nil {
- // This function will be called again if this is an
- // IsAborted error.
- return err
- }
- if err := row.Column(0, &balance); err != nil {
- return err
- }
-
- if balance <= 10 {
- return errors.New("insufficient funds in account")
- }
- balance -= 10
- m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance})
- return txn.BufferWrite([]*spanner.Mutation{m})
- // The buffered mutation will be committed. If the commit
- // fails with an IsAborted error, this function will be called
- // again.
- })
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-func ExampleUpdate() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
- row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
- if err != nil {
- return err
- }
- var balance int64
- if err := row.Column(0, &balance); err != nil {
- return err
- }
- return txn.BufferWrite([]*spanner.Mutation{
- spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance + 10}),
- })
- })
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-// This example is the same as the one for Update, except for the use of UpdateMap.
-func ExampleUpdateMap() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
- row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
- if err != nil {
- return err
- }
- var balance int64
- if err := row.Column(0, &balance); err != nil {
- return err
- }
- return txn.BufferWrite([]*spanner.Mutation{
- spanner.UpdateMap("Accounts", map[string]interface{}{
- "user": "alice",
- "balance": balance + 10,
- }),
- })
- })
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-// This example is the same as the one for Update, except for the use of UpdateStruct.
-func ExampleUpdateStruct() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- type account struct {
- User string `spanner:"user"`
- Balance int64 `spanner:"balance"`
- }
- _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
- row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
- if err != nil {
- return err
- }
- var balance int64
- if err := row.Column(0, &balance); err != nil {
- return err
- }
- m, err := spanner.UpdateStruct("Accounts", account{
- User: "alice",
- Balance: balance + 10,
- })
- if err != nil {
- return err
- }
- return txn.BufferWrite([]*spanner.Mutation{m})
- })
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-func ExampleClient_Apply() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- m := spanner.Update("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"})
- _, err = client.Apply(ctx, []*spanner.Mutation{m})
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-func ExampleInsert() {
- m := spanner.Insert("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"})
- _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
-}
-
-func ExampleInsertMap() {
- m := spanner.InsertMap("Users", map[string]interface{}{
- "name": "alice",
- "email": "a@example.com",
- })
- _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
-}
-
-func ExampleInsertStruct() {
- type User struct {
- Name, Email string
- }
- u := User{Name: "alice", Email: "a@example.com"}
- m, err := spanner.InsertStruct("Users", u)
- if err != nil {
- // TODO: Handle error.
- }
- _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
-}
-
-func ExampleDelete() {
- m := spanner.Delete("Users", spanner.Key{"alice"})
- _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
-}
-
-func ExampleDelete_KeyRange() {
- m := spanner.Delete("Users", spanner.KeyRange{
- Start: spanner.Key{"alice"},
- End: spanner.Key{"bob"},
- Kind: spanner.ClosedClosed,
- })
- _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
-}
-
-func ExampleRowIterator_Next() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
- defer iter.Stop()
- for {
- row, err := iter.Next()
- if err == iterator.Done {
- break
- }
- if err != nil {
- // TODO: Handle error.
- }
- var firstName string
- if err := row.Column(0, &firstName); err != nil {
- // TODO: Handle error.
- }
- fmt.Println(firstName)
- }
-}
-
-func ExampleRowIterator_Do() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
- err = iter.Do(func(r *spanner.Row) error {
- var firstName string
- if err := r.Column(0, &firstName); err != nil {
- return err
- }
- fmt.Println(firstName)
- return nil
- })
- if err != nil {
- // TODO: Handle error.
- }
-}
-
-func ExampleRow_Size() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
- fmt.Println(row.Size()) // size is 2
-}
-
-func ExampleRow_ColumnName() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
- fmt.Println(row.ColumnName(1)) // prints "balance"
-}
-
-func ExampleRow_ColumnIndex() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
- index, err := row.ColumnIndex("balance")
- if err != nil {
- // TODO: Handle error.
- }
- fmt.Println(index)
-}
-
-func ExampleRow_ColumnNames() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
- fmt.Println(row.ColumnNames())
-}
-
-func ExampleRow_ColumnByName() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
- var balance int64
- if err := row.ColumnByName("balance", &balance); err != nil {
- // TODO: Handle error.
- }
- fmt.Println(balance)
-}
-
-func ExampleRow_Columns() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
- var name string
- var balance int64
- if err := row.Columns(&name, &balance); err != nil {
- // TODO: Handle error.
- }
- fmt.Println(name, balance)
-}
-
-func ExampleRow_ToStruct() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
- if err != nil {
- // TODO: Handle error.
- }
-
- type Account struct {
- Name string
- Balance int64
- }
-
- var acct Account
- if err := row.ToStruct(&acct); err != nil {
- // TODO: Handle error.
- }
- fmt.Println(acct)
-}
-
-func ExampleReadOnlyTransaction_Read() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- iter := client.Single().Read(ctx, "Users",
- spanner.KeySets(spanner.Key{"alice"}, spanner.Key{"bob"}),
- []string{"name", "email"})
- _ = iter // TODO: iterate using Next or Do.
-}
-
-func ExampleReadOnlyTransaction_ReadUsingIndex() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- iter := client.Single().ReadUsingIndex(ctx, "Users",
- "UsersByEmail",
- spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}),
- []string{"name", "email"})
- _ = iter // TODO: iterate using Next or Do.
-}
-
-func ExampleReadOnlyTransaction_ReadRow() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- row, err := client.Single().ReadRow(ctx, "Users", spanner.Key{"alice"},
- []string{"name", "email"})
- if err != nil {
- // TODO: Handle error.
- }
- _ = row // TODO: use row
-}
-
-func ExampleReadOnlyTransaction_Query() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
- _ = iter // TODO: iterate using Next or Do.
-}
-
-func ExampleNewStatement() {
- stmt := spanner.NewStatement("SELECT FirstName, LastName FROM SINGERS WHERE LastName >= @start")
- stmt.Params["start"] = "Dylan"
- // TODO: Use stmt in Query.
-}
-
-func ExampleNewStatement_structLiteral() {
- stmt := spanner.Statement{
- SQL: "SELECT FirstName, LastName FROM SINGERS WHERE LastName >= @start",
- Params: map[string]interface{}{"start": "Dylan"},
- }
- _ = stmt // TODO: Use stmt in Query.
-}
-
-func ExampleReadOnlyTransaction_Timestamp() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- txn := client.Single()
- row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"},
- []string{"name", "email"})
- if err != nil {
- // TODO: Handle error.
- }
- readTimestamp, err := txn.Timestamp()
- if err != nil {
- // TODO: Handle error.
- }
- fmt.Println("read happened at", readTimestamp)
- _ = row // TODO: use row
-}
-
-func ExampleReadOnlyTransaction_WithTimestampBound() {
- ctx := context.Background()
- client, err := spanner.NewClient(ctx, myDB)
- if err != nil {
- // TODO: Handle error.
- }
- txn := client.Single().WithTimestampBound(spanner.MaxStaleness(30 * time.Second))
- row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"}, []string{"name", "email"})
- if err != nil {
- // TODO: Handle error.
- }
- _ = row // TODO: use row
- readTimestamp, err := txn.Timestamp()
- if err != nil {
- // TODO: Handle error.
- }
- fmt.Println("read happened at", readTimestamp)
-}
-
-func ExampleNewGenericColumnValue_Decode() {
- // In real applications, rows can be retrieved by methods like client.Single().ReadRow().
- row, err := spanner.NewRow([]string{"intCol", "strCol"}, []interface{}{42, "my-text"})
- if err != nil {
- // TODO: Handle error.
- }
- for i := 0; i < row.Size(); i++ {
- var col spanner.GenericColumnValue
- if err := row.Column(i, &col); err != nil {
- // TODO: Handle error.
- }
- switch col.Type.Code {
- case sppb.TypeCode_INT64:
- var v int64
- if err := col.Decode(&v); err != nil {
- // TODO: Handle error.
- }
- fmt.Println("int", v)
- case sppb.TypeCode_STRING:
- var v string
- if err := col.Decode(&v); err != nil {
- // TODO: Handle error.
- }
- fmt.Println("string", v)
- }
- }
- // Output:
- // int 42
- // string my-text
-}
diff --git a/vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go b/vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go
deleted file mode 100644
index f278c7cc6..000000000
--- a/vendor/cloud.google.com/go/spanner/internal/testutil/mockclient.go
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package testutil
-
-import (
- "errors"
- "fmt"
- "reflect"
- "sync"
- "testing"
- "time"
-
- "golang.org/x/net/context"
-
- "github.com/golang/protobuf/ptypes/empty"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- pbt "github.com/golang/protobuf/ptypes/timestamp"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
-)
-
-// Action is a mocked RPC activity that MockCloudSpannerClient will take.
-type Action struct {
- method string
- err error
-}
-
-// NewAction creates Action objects.
-func NewAction(m string, e error) Action {
- return Action{m, e}
-}
-
-// MockCloudSpannerClient is a mock implementation of sppb.SpannerClient.
-type MockCloudSpannerClient struct {
- mu sync.Mutex
- t *testing.T
- // Live sessions on the client.
- sessions map[string]bool
- // Expected set of actions that will be executed by the client.
- actions []Action
- // Session ping history
- pings []string
- // Injected error, will be returned by all APIs
- injErr map[string]error
- // nice client will not fail on any request
- nice bool
-}
-
-// NewMockCloudSpannerClient creates new MockCloudSpannerClient instance.
-func NewMockCloudSpannerClient(t *testing.T, acts ...Action) *MockCloudSpannerClient {
- mc := &MockCloudSpannerClient{t: t, sessions: map[string]bool{}, injErr: map[string]error{}}
- mc.SetActions(acts...)
- return mc
-}
-
-// MakeNice makes this a nice mock which will not fail on any request.
-func (m *MockCloudSpannerClient) MakeNice() {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.nice = true
-}
-
-// MakeStrict makes this a strict mock which will fail on any unexpected request.
-func (m *MockCloudSpannerClient) MakeStrict() {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.nice = false
-}
-
-// InjectError injects a global error that will be returned by all APIs regardless of
-// the actions array.
-func (m *MockCloudSpannerClient) InjectError(method string, err error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.injErr[method] = err
-}
-
-// SetActions sets the new set of expected actions to MockCloudSpannerClient.
-func (m *MockCloudSpannerClient) SetActions(acts ...Action) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.actions = []Action{}
- for _, act := range acts {
- m.actions = append(m.actions, act)
- }
-}
-
-// DumpPings dumps the ping history.
-func (m *MockCloudSpannerClient) DumpPings() []string {
- m.mu.Lock()
- defer m.mu.Unlock()
- return append([]string(nil), m.pings...)
-}
-
-// DumpSessions dumps the internal session table.
-func (m *MockCloudSpannerClient) DumpSessions() map[string]bool {
- m.mu.Lock()
- defer m.mu.Unlock()
- st := map[string]bool{}
- for s, v := range m.sessions {
- st[s] = v
- }
- return st
-}
-
-// CreateSession is a placeholder for SpannerClient.CreateSession.
-func (m *MockCloudSpannerClient) CreateSession(c context.Context, r *sppb.CreateSessionRequest, opts ...grpc.CallOption) (*sppb.Session, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if err := m.injErr["CreateSession"]; err != nil {
- return nil, err
- }
- s := &sppb.Session{}
- if r.Database != "mockdb" {
- // Reject other databases
- return s, grpc.Errorf(codes.NotFound, fmt.Sprintf("database not found: %v", r.Database))
- }
- // Generate & record session name.
- s.Name = fmt.Sprintf("mockdb-%v", time.Now().UnixNano())
- m.sessions[s.Name] = true
- return s, nil
-}
-
-// GetSession is a placeholder for SpannerClient.GetSession.
-func (m *MockCloudSpannerClient) GetSession(c context.Context, r *sppb.GetSessionRequest, opts ...grpc.CallOption) (*sppb.Session, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if err := m.injErr["GetSession"]; err != nil {
- return nil, err
- }
- m.pings = append(m.pings, r.Name)
- if _, ok := m.sessions[r.Name]; !ok {
- return nil, grpc.Errorf(codes.NotFound, fmt.Sprintf("Session not found: %v", r.Name))
- }
- return &sppb.Session{Name: r.Name}, nil
-}
-
-// DeleteSession is a placeholder for SpannerClient.DeleteSession.
-func (m *MockCloudSpannerClient) DeleteSession(c context.Context, r *sppb.DeleteSessionRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if err := m.injErr["DeleteSession"]; err != nil {
- return nil, err
- }
- if _, ok := m.sessions[r.Name]; !ok {
- // Session not found.
- return &empty.Empty{}, grpc.Errorf(codes.NotFound, fmt.Sprintf("Session not found: %v", r.Name))
- }
- // Delete session from in-memory table.
- delete(m.sessions, r.Name)
- return &empty.Empty{}, nil
-}
-
-// ExecuteSql is a placeholder for SpannerClient.ExecuteSql.
-func (m *MockCloudSpannerClient) ExecuteSql(c context.Context, r *sppb.ExecuteSqlRequest, opts ...grpc.CallOption) (*sppb.ResultSet, error) {
- return nil, errors.New("Unimplemented")
-}
-
-// ExecuteStreamingSql is a mock implementation of SpannerClient.ExecuteStreamingSql.
-func (m *MockCloudSpannerClient) ExecuteStreamingSql(c context.Context, r *sppb.ExecuteSqlRequest, opts ...grpc.CallOption) (sppb.Spanner_ExecuteStreamingSqlClient, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if err := m.injErr["ExecuteStreamingSql"]; err != nil {
- return nil, err
- }
- if len(m.actions) == 0 {
- m.t.Fatalf("unexpected ExecuteStreamingSql executed")
- }
- act := m.actions[0]
- m.actions = m.actions[1:]
- if act.method != "ExecuteStreamingSql" {
- m.t.Fatalf("unexpected ExecuteStreamingSql call, want action: %v", act)
- }
- wantReq := &sppb.ExecuteSqlRequest{
- Session: "mocksession",
- Transaction: &sppb.TransactionSelector{
- Selector: &sppb.TransactionSelector_SingleUse{
- SingleUse: &sppb.TransactionOptions{
- Mode: &sppb.TransactionOptions_ReadOnly_{
- ReadOnly: &sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{
- Strong: true,
- },
- ReturnReadTimestamp: false,
- },
- },
- },
- },
- },
- Sql: "mockquery",
- Params: &proto3.Struct{
- Fields: map[string]*proto3.Value{"var1": &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: "abc"}}},
- },
- ParamTypes: map[string]*sppb.Type{"var1": &sppb.Type{Code: sppb.TypeCode_STRING}},
- }
- if !reflect.DeepEqual(r, wantReq) {
- return nil, fmt.Errorf("got query request: %v, want: %v", r, wantReq)
- }
- if act.err != nil {
- return nil, act.err
- }
- return nil, errors.New("query never succeeds on mock client")
-}
-
-// Read is a placeholder for SpannerClient.Read.
-func (m *MockCloudSpannerClient) Read(c context.Context, r *sppb.ReadRequest, opts ...grpc.CallOption) (*sppb.ResultSet, error) {
- m.t.Fatalf("Read is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// StreamingRead is a placeholder for SpannerClient.StreamingRead.
-func (m *MockCloudSpannerClient) StreamingRead(c context.Context, r *sppb.ReadRequest, opts ...grpc.CallOption) (sppb.Spanner_StreamingReadClient, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if err := m.injErr["StreamingRead"]; err != nil {
- return nil, err
- }
- if len(m.actions) == 0 {
- m.t.Fatalf("unexpected StreamingRead executed")
- }
- act := m.actions[0]
- m.actions = m.actions[1:]
- if act.method != "StreamingRead" && act.method != "StreamingIndexRead" {
- m.t.Fatalf("unexpected read call, want action: %v", act)
- }
- wantReq := &sppb.ReadRequest{
- Session: "mocksession",
- Transaction: &sppb.TransactionSelector{
- Selector: &sppb.TransactionSelector_SingleUse{
- SingleUse: &sppb.TransactionOptions{
- Mode: &sppb.TransactionOptions_ReadOnly_{
- ReadOnly: &sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{
- Strong: true,
- },
- ReturnReadTimestamp: false,
- },
- },
- },
- },
- },
- Table: "t_mock",
- Columns: []string{"col1", "col2"},
- KeySet: &sppb.KeySet{
- []*proto3.ListValue{
- &proto3.ListValue{
- Values: []*proto3.Value{
- &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- },
- },
- },
- []*sppb.KeyRange{},
- false,
- },
- }
- if act.method == "StreamingIndexRead" {
- wantReq.Index = "idx1"
- }
- if !reflect.DeepEqual(r, wantReq) {
- return nil, fmt.Errorf("got query request: %v, want: %v", r, wantReq)
- }
- if act.err != nil {
- return nil, act.err
- }
- return nil, errors.New("read never succeeds on mock client")
-}
-
-// BeginTransaction is a placeholder for SpannerClient.BeginTransaction.
-func (m *MockCloudSpannerClient) BeginTransaction(c context.Context, r *sppb.BeginTransactionRequest, opts ...grpc.CallOption) (*sppb.Transaction, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if !m.nice {
- if err := m.injErr["BeginTransaction"]; err != nil {
- return nil, err
- }
- if len(m.actions) == 0 {
- m.t.Fatalf("unexpected Begin executed")
- }
- act := m.actions[0]
- m.actions = m.actions[1:]
- if act.method != "Begin" {
- m.t.Fatalf("unexpected Begin call, want action: %v", act)
- }
- if act.err != nil {
- return nil, act.err
- }
- }
- resp := &sppb.Transaction{Id: []byte("transaction-1")}
- if _, ok := r.Options.Mode.(*sppb.TransactionOptions_ReadOnly_); ok {
- resp.ReadTimestamp = &pbt.Timestamp{Seconds: 3, Nanos: 4}
- }
- return resp, nil
-}
-
-// Commit is a placeholder for SpannerClient.Commit.
-func (m *MockCloudSpannerClient) Commit(c context.Context, r *sppb.CommitRequest, opts ...grpc.CallOption) (*sppb.CommitResponse, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if !m.nice {
- if err := m.injErr["Commit"]; err != nil {
- return nil, err
- }
- if len(m.actions) == 0 {
- m.t.Fatalf("unexpected Commit executed")
- }
- act := m.actions[0]
- m.actions = m.actions[1:]
- if act.method != "Commit" {
- m.t.Fatalf("unexpected Commit call, want action: %v", act)
- }
- if act.err != nil {
- return nil, act.err
- }
- }
- return &sppb.CommitResponse{CommitTimestamp: &pbt.Timestamp{Seconds: 1, Nanos: 2}}, nil
-}
-
-// Rollback is a placeholder for SpannerClient.Rollback.
-func (m *MockCloudSpannerClient) Rollback(c context.Context, r *sppb.RollbackRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if !m.nice {
- if err := m.injErr["Rollback"]; err != nil {
- return nil, err
- }
- if len(m.actions) == 0 {
- m.t.Fatalf("unexpected Rollback executed")
- }
- act := m.actions[0]
- m.actions = m.actions[1:]
- if act.method != "Rollback" {
- m.t.Fatalf("unexpected Rollback call, want action: %v", act)
- }
- if act.err != nil {
- return nil, act.err
- }
- }
- return nil, nil
-}
diff --git a/vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go b/vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go
deleted file mode 100644
index 7a04e7f7f..000000000
--- a/vendor/cloud.google.com/go/spanner/internal/testutil/mockserver.go
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package testutil
-
-import (
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "net"
- "testing"
- "time"
-
- "golang.org/x/net/context"
-
- "github.com/golang/protobuf/ptypes/empty"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- pbt "github.com/golang/protobuf/ptypes/timestamp"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
-)
-
-var (
- // KvMeta is the Metadata for mocked KV table.
- KvMeta = sppb.ResultSetMetadata{
- RowType: &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- {
- Name: "Key",
- Type: &sppb.Type{Code: sppb.TypeCode_STRING},
- },
- {
- Name: "Value",
- Type: &sppb.Type{Code: sppb.TypeCode_STRING},
- },
- },
- },
- }
-)
-
-// MockCtlMsg encapsulates PartialResultSet/error that might be sent to
-// client
-type MockCtlMsg struct {
- // If ResumeToken == true, mock server will generate a row with
- // resume token.
- ResumeToken bool
- // If Err != nil, mock server will return error in RPC response.
- Err error
-}
-
-// MockCloudSpanner is a mock implementation of SpannerServer interface.
-// TODO: make MockCloudSpanner a full-fleged Cloud Spanner implementation.
-type MockCloudSpanner struct {
- s *grpc.Server
- t *testing.T
- addr string
- msgs chan MockCtlMsg
- readTs time.Time
- next int
-}
-
-// Addr returns the listening address of mock server.
-func (m *MockCloudSpanner) Addr() string {
- return m.addr
-}
-
-// AddMsg generates a new mocked row which can be received by client.
-func (m *MockCloudSpanner) AddMsg(err error, resumeToken bool) {
- msg := MockCtlMsg{
- ResumeToken: resumeToken,
- Err: err,
- }
- if err == io.EOF {
- close(m.msgs)
- } else {
- m.msgs <- msg
- }
-}
-
-// Done signals an end to a mocked stream.
-func (m *MockCloudSpanner) Done() {
- close(m.msgs)
-}
-
-// CreateSession is a placeholder for SpannerServer.CreateSession.
-func (m *MockCloudSpanner) CreateSession(c context.Context, r *sppb.CreateSessionRequest) (*sppb.Session, error) {
- m.t.Fatalf("CreateSession is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// GetSession is a placeholder for SpannerServer.GetSession.
-func (m *MockCloudSpanner) GetSession(c context.Context, r *sppb.GetSessionRequest) (*sppb.Session, error) {
- m.t.Fatalf("GetSession is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// DeleteSession is a placeholder for SpannerServer.DeleteSession.
-func (m *MockCloudSpanner) DeleteSession(c context.Context, r *sppb.DeleteSessionRequest) (*empty.Empty, error) {
- m.t.Fatalf("DeleteSession is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// ExecuteSql is a placeholder for SpannerServer.ExecuteSql.
-func (m *MockCloudSpanner) ExecuteSql(c context.Context, r *sppb.ExecuteSqlRequest) (*sppb.ResultSet, error) {
- m.t.Fatalf("ExecuteSql is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// EncodeResumeToken return mock resume token encoding for an uint64 integer.
-func EncodeResumeToken(t uint64) []byte {
- rt := make([]byte, 16)
- binary.PutUvarint(rt, t)
- return rt
-}
-
-// DecodeResumeToken decodes a mock resume token into an uint64 integer.
-func DecodeResumeToken(t []byte) (uint64, error) {
- s, n := binary.Uvarint(t)
- if n <= 0 {
- return 0, fmt.Errorf("invalid resume token: %v", t)
- }
- return s, nil
-}
-
-// ExecuteStreamingSql is a mock implementation of SpannerServer.ExecuteStreamingSql.
-func (m *MockCloudSpanner) ExecuteStreamingSql(r *sppb.ExecuteSqlRequest, s sppb.Spanner_ExecuteStreamingSqlServer) error {
- switch r.Sql {
- case "SELECT * from t_unavailable":
- return grpc.Errorf(codes.Unavailable, "mock table unavailable")
- case "SELECT t.key key, t.value value FROM t_mock t":
- if r.ResumeToken != nil {
- s, err := DecodeResumeToken(r.ResumeToken)
- if err != nil {
- return err
- }
- m.next = int(s) + 1
- }
- for {
- msg, more := <-m.msgs
- if !more {
- break
- }
- if msg.Err == nil {
- var rt []byte
- if msg.ResumeToken {
- rt = EncodeResumeToken(uint64(m.next))
- }
- meta := KvMeta
- meta.Transaction = &sppb.Transaction{
- ReadTimestamp: &pbt.Timestamp{
- Seconds: m.readTs.Unix(),
- Nanos: int32(m.readTs.Nanosecond()),
- },
- }
- err := s.Send(&sppb.PartialResultSet{
- Metadata: &meta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: fmt.Sprintf("foo-%02d", m.next)}},
- {Kind: &proto3.Value_StringValue{StringValue: fmt.Sprintf("bar-%02d", m.next)}},
- },
- ResumeToken: rt,
- })
- m.next = m.next + 1
- if err != nil {
- return err
- }
- continue
- }
- return msg.Err
- }
- return nil
- default:
- return fmt.Errorf("unsupported SQL: %v", r.Sql)
- }
-}
-
-// Read is a placeholder for SpannerServer.Read.
-func (m *MockCloudSpanner) Read(c context.Context, r *sppb.ReadRequest) (*sppb.ResultSet, error) {
- m.t.Fatalf("Read is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// StreamingRead is a placeholder for SpannerServer.StreamingRead.
-func (m *MockCloudSpanner) StreamingRead(r *sppb.ReadRequest, s sppb.Spanner_StreamingReadServer) error {
- m.t.Fatalf("StreamingRead is unimplemented")
- return errors.New("Unimplemented")
-}
-
-// BeginTransaction is a placeholder for SpannerServer.BeginTransaction.
-func (m *MockCloudSpanner) BeginTransaction(c context.Context, r *sppb.BeginTransactionRequest) (*sppb.Transaction, error) {
- m.t.Fatalf("BeginTransaction is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// Commit is a placeholder for SpannerServer.Commit.
-func (m *MockCloudSpanner) Commit(c context.Context, r *sppb.CommitRequest) (*sppb.CommitResponse, error) {
- m.t.Fatalf("Commit is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// Rollback is a placeholder for SpannerServer.Rollback.
-func (m *MockCloudSpanner) Rollback(c context.Context, r *sppb.RollbackRequest) (*empty.Empty, error) {
- m.t.Fatalf("Rollback is unimplemented")
- return nil, errors.New("Unimplemented")
-}
-
-// Serve runs a MockCloudSpanner listening on a random localhost address.
-func (m *MockCloudSpanner) Serve() {
- m.s = grpc.NewServer()
- if m.addr == "" {
- m.addr = "localhost:0"
- }
- lis, err := net.Listen("tcp", m.addr)
- if err != nil {
- m.t.Fatalf("Failed to listen: %v", err)
- }
- go m.s.Serve(lis)
- _, port, err := net.SplitHostPort(lis.Addr().String())
- if err != nil {
- m.t.Fatalf("Failed to parse listener address: %v", err)
- }
- sppb.RegisterSpannerServer(m.s, m)
- m.addr = "localhost:" + port
-}
-
-// Stop terminates MockCloudSpanner and closes the serving port.
-func (m *MockCloudSpanner) Stop() {
- m.s.Stop()
-}
-
-// NewMockCloudSpanner creates a new MockCloudSpanner instance.
-func NewMockCloudSpanner(t *testing.T, ts time.Time) *MockCloudSpanner {
- mcs := &MockCloudSpanner{
- t: t,
- msgs: make(chan MockCtlMsg, 1000),
- readTs: ts,
- }
- return mcs
-}
diff --git a/vendor/cloud.google.com/go/spanner/key.go b/vendor/cloud.google.com/go/spanner/key.go
deleted file mode 100644
index 1b780deff..000000000
--- a/vendor/cloud.google.com/go/spanner/key.go
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "bytes"
- "fmt"
- "time"
-
- "google.golang.org/grpc/codes"
-
- "cloud.google.com/go/civil"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// A Key can be either a Cloud Spanner row's primary key or a secondary index key.
-// It is essentially an interface{} array, which represents a set of Cloud Spanner
-// columns. A Key type has the following usages:
-//
-// - Used as primary key which uniquely identifies a Cloud Spanner row.
-// - Used as secondary index key which maps to a set of Cloud Spanner rows
-// indexed under it.
-// - Used as endpoints of primary key/secondary index ranges,
-// see also the KeyRange type.
-//
-// Rows that are identified by the Key type are outputs of read operation or targets of
-// delete operation in a mutation. Note that for Insert/Update/InsertOrUpdate/Update
-// mutation types, although they don't require a primary key explicitly, the column list
-// provided must contain enough columns that can comprise a primary key.
-//
-// Keys are easy to construct. For example, suppose you have a table with a
-// primary key of username and product ID. To make a key for this table:
-//
-// key := spanner.Key{"john", 16}
-//
-// See the description of Row and Mutation types for how Go types are
-// mapped to Cloud Spanner types. For convenience, Key type supports a wide range
-// of Go types:
-// - int, int8, int16, int32, int64, and NullInt64 are mapped to Cloud Spanner's INT64 type.
-// - uint8, uint16 and uint32 are also mapped to Cloud Spanner's INT64 type.
-// - float32, float64, NullFloat64 are mapped to Cloud Spanner's FLOAT64 type.
-// - bool and NullBool are mapped to Cloud Spanner's BOOL type.
-// - []byte is mapped to Cloud Spanner's BYTES type.
-// - string and NullString are mapped to Cloud Spanner's STRING type.
-// - time.Time and NullTime are mapped to Cloud Spanner's TIMESTAMP type.
-// - civil.Date and NullDate are mapped to Cloud Spanner's DATE type.
-type Key []interface{}
-
-// errInvdKeyPartType returns error for unsupported key part type.
-func errInvdKeyPartType(part interface{}) error {
- return spannerErrorf(codes.InvalidArgument, "key part has unsupported type %T", part)
-}
-
-// keyPartValue converts a part of the Key (which is a valid Cloud Spanner type)
-// into a proto3.Value. Used for encoding Key type into protobuf.
-func keyPartValue(part interface{}) (pb *proto3.Value, err error) {
- switch v := part.(type) {
- case int:
- pb, _, err = encodeValue(int64(v))
- case int8:
- pb, _, err = encodeValue(int64(v))
- case int16:
- pb, _, err = encodeValue(int64(v))
- case int32:
- pb, _, err = encodeValue(int64(v))
- case uint8:
- pb, _, err = encodeValue(int64(v))
- case uint16:
- pb, _, err = encodeValue(int64(v))
- case uint32:
- pb, _, err = encodeValue(int64(v))
- case float32:
- pb, _, err = encodeValue(float64(v))
- case int64, float64, NullInt64, NullFloat64, bool, NullBool, []byte, string, NullString, time.Time, civil.Date, NullTime, NullDate:
- pb, _, err = encodeValue(v)
- default:
- return nil, errInvdKeyPartType(v)
- }
- return pb, err
-}
-
-// proto converts a spanner.Key into a proto3.ListValue.
-func (key Key) proto() (*proto3.ListValue, error) {
- lv := &proto3.ListValue{}
- lv.Values = make([]*proto3.Value, 0, len(key))
- for _, part := range key {
- v, err := keyPartValue(part)
- if err != nil {
- return nil, err
- }
- lv.Values = append(lv.Values, v)
- }
- return lv, nil
-}
-
-// keySetProto lets a single Key act as a KeySet.
-func (key Key) keySetProto() (*sppb.KeySet, error) {
- kp, err := key.proto()
- if err != nil {
- return nil, err
- }
- return &sppb.KeySet{Keys: []*proto3.ListValue{kp}}, nil
-}
-
-// String implements fmt.Stringer for Key. For string, []byte and NullString, it
-// prints the uninterpreted bytes of their contents, leaving caller with the
-// opportunity to escape the output.
-func (key Key) String() string {
- b := &bytes.Buffer{}
- fmt.Fprint(b, "(")
- for i, part := range []interface{}(key) {
- if i != 0 {
- fmt.Fprint(b, ",")
- }
- switch v := part.(type) {
- case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, float32, float64, bool:
- // Use %v to print numeric types and bool.
- fmt.Fprintf(b, "%v", v)
- case string:
- fmt.Fprintf(b, "%q", v)
- case []byte:
- if v != nil {
- fmt.Fprintf(b, "%q", v)
- } else {
- fmt.Fprint(b, "<null>")
- }
- case NullInt64, NullFloat64, NullBool, NullString, NullTime, NullDate:
- // The above types implement fmt.Stringer.
- fmt.Fprintf(b, "%s", v)
- case civil.Date:
- fmt.Fprintf(b, "%q", v)
- case time.Time:
- fmt.Fprintf(b, "%q", v.Format(time.RFC3339Nano))
- default:
- fmt.Fprintf(b, "%v", v)
- }
- }
- fmt.Fprint(b, ")")
- return b.String()
-}
-
-// AsPrefix returns a KeyRange for all keys where k is the prefix.
-func (k Key) AsPrefix() KeyRange {
- return KeyRange{
- Start: k,
- End: k,
- Kind: ClosedClosed,
- }
-}
-
-// KeyRangeKind describes the kind of interval represented by a KeyRange:
-// whether it is open or closed on the left and right.
-type KeyRangeKind int
-
-const (
- // ClosedOpen is closed on the left and open on the right: the Start
- // key is included, the End key is excluded.
- ClosedOpen KeyRangeKind = iota
-
- // ClosedClosed is closed on the left and the right: both keys are included.
- ClosedClosed
-
- // OpenClosed is open on the left and closed on the right: the Start
- // key is excluded, the End key is included.
- OpenClosed
-
- // OpenOpen is open on the left and the right: neither key is included.
- OpenOpen
-)
-
-// A KeyRange represents a range of rows in a table or index.
-//
-// A range has a Start key and an End key. IncludeStart and IncludeEnd
-// indicate whether the Start and End keys are included in the range.
-//
-// 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:
-//
-// spanner.Key{"Bob", "2014-09-23"}
-// spanner.Key{"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:
-//
-// spanner.KeyRange{
-// Start: spanner.Key{"Bob", "2015-01-01"},
-// End: spanner.Key{"Bob", "2015-12-31"},
-// Kind: ClosedClosed,
-// }
-//
-// 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 IncludeStart is true, then rows that exactly match the
-// provided components of the Start key are included; if IncludeStart is false
-// then rows that exactly match are not included. IncludeEnd and End key
-// behave in the same fashion.
-//
-// For example, the following range includes all events for "Bob" that occurred
-// during and after the year 2000:
-//
-// spanner.KeyRange{
-// Start: spanner.Key{"Bob", "2000-01-01"},
-// End: spanner.Key{"Bob"},
-// Kind: ClosedClosed,
-// }
-//
-// The next example retrieves all events for "Bob":
-//
-// spanner.Key{"Bob"}.AsPrefix()
-//
-// To retrieve events before the year 2000:
-//
-// spanner.KeyRange{
-// Start: spanner.Key{"Bob"},
-// End: spanner.Key{"Bob", "2000-01-01"},
-// Kind: ClosedOpen,
-// }
-//
-// Although we specified a Kind for this KeyRange, we didn't need to, because
-// the default is ClosedOpen. In later examples we'll omit Kind if it is
-// ClosedOpen.
-//
-// The following range includes all rows in a table or under a
-// index:
-//
-// spanner.AllKeys()
-//
-// This range returns all users whose UserName begins with any
-// character from A to C:
-//
-// spanner.KeyRange{
-// Start: spanner.Key{"A"},
-// End: spanner.Key{"D"},
-// }
-//
-// This range returns all users whose UserName begins with B:
-//
-// spanner.KeyRange{
-// Start: spanner.Key{"B"},
-// End: spanner.Key{"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:
-//
-// spanner.KeyRange{
-// Start: spanner.Key{100},
-// End: spanner.Key{1},
-// Kind: ClosedClosed,
-// }
-//
-// Note that 100 is passed as the start, and 1 is passed as the end, because
-// Key is a descending column in the schema.
-type KeyRange struct {
- // Start specifies the left boundary of the key range; End specifies
- // the right boundary of the key range.
- Start, End Key
-
- // Kind describes whether the boundaries of the key range include
- // their keys.
- Kind KeyRangeKind
-}
-
-// String implements fmt.Stringer for KeyRange type.
-func (r KeyRange) String() string {
- var left, right string
- switch r.Kind {
- case ClosedClosed:
- left, right = "[", "]"
- case ClosedOpen:
- left, right = "[", ")"
- case OpenClosed:
- left, right = "(", "]"
- case OpenOpen:
- left, right = "(", ")"
- default:
- left, right = "?", "?"
- }
- return fmt.Sprintf("%s%s,%s%s", left, r.Start, r.End, right)
-}
-
-// proto converts KeyRange into sppb.KeyRange.
-func (r KeyRange) proto() (*sppb.KeyRange, error) {
- var err error
- var start, end *proto3.ListValue
- pb := &sppb.KeyRange{}
- if start, err = r.Start.proto(); err != nil {
- return nil, err
- }
- if end, err = r.End.proto(); err != nil {
- return nil, err
- }
- if r.Kind == ClosedClosed || r.Kind == ClosedOpen {
- pb.StartKeyType = &sppb.KeyRange_StartClosed{StartClosed: start}
- } else {
- pb.StartKeyType = &sppb.KeyRange_StartOpen{StartOpen: start}
- }
- if r.Kind == ClosedClosed || r.Kind == OpenClosed {
- pb.EndKeyType = &sppb.KeyRange_EndClosed{EndClosed: end}
- } else {
- pb.EndKeyType = &sppb.KeyRange_EndOpen{EndOpen: end}
- }
- return pb, nil
-}
-
-// keySetProto lets a KeyRange act as a KeySet.
-func (r KeyRange) keySetProto() (*sppb.KeySet, error) {
- rp, err := r.proto()
- if err != nil {
- return nil, err
- }
- return &sppb.KeySet{Ranges: []*sppb.KeyRange{rp}}, nil
-}
-
-// A 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.
-//
-// An individual Key can act as a KeySet, as can a KeyRange. Use the KeySets function
-// to create a KeySet consisting of multiple Keys and KeyRanges. To obtain an empty
-// KeySet, call KeySets with no arguments.
-//
-// 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), the Cloud Spanner backend behaves
-// as if the key were only specified once.
-type KeySet interface {
- keySetProto() (*sppb.KeySet, error)
-}
-
-// AllKeys returns a KeySet that represents all Keys of a table or a index.
-func AllKeys() KeySet {
- return all{}
-}
-
-type all struct{}
-
-func (all) keySetProto() (*sppb.KeySet, error) {
- return &sppb.KeySet{All: true}, nil
-}
-
-// KeySets returns the union of the KeySets. If any of the KeySets is AllKeys, then
-// the resulting KeySet will be equivalent to AllKeys.
-func KeySets(keySets ...KeySet) KeySet {
- u := make(union, len(keySets))
- copy(u, keySets)
- return u
-}
-
-type union []KeySet
-
-func (u union) keySetProto() (*sppb.KeySet, error) {
- upb := &sppb.KeySet{}
- for _, ks := range u {
- pb, err := ks.keySetProto()
- if err != nil {
- return nil, err
- }
- if pb.All {
- return pb, nil
- }
- upb.Keys = append(upb.Keys, pb.Keys...)
- upb.Ranges = append(upb.Ranges, pb.Ranges...)
- }
- return upb, nil
-}
diff --git a/vendor/cloud.google.com/go/spanner/key_test.go b/vendor/cloud.google.com/go/spanner/key_test.go
deleted file mode 100644
index e552e8647..000000000
--- a/vendor/cloud.google.com/go/spanner/key_test.go
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "reflect"
- "testing"
- "time"
-
- "cloud.google.com/go/civil"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// Test Key.String() and Key.proto().
-func TestKey(t *testing.T) {
- tm, _ := time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z")
- dt, _ := civil.ParseDate("2016-11-15")
- for _, test := range []struct {
- k Key
- wantProto *proto3.ListValue
- wantStr string
- }{
- {
- k: Key{int(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{int8(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{int16(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{int32(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{int64(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{uint8(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{uint16(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{uint32(1)},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{true},
- wantProto: listValueProto(boolProto(true)),
- wantStr: "(true)",
- },
- {
- k: Key{float32(1.5)},
- wantProto: listValueProto(floatProto(1.5)),
- wantStr: "(1.5)",
- },
- {
- k: Key{float64(1.5)},
- wantProto: listValueProto(floatProto(1.5)),
- wantStr: "(1.5)",
- },
- {
- k: Key{"value"},
- wantProto: listValueProto(stringProto("value")),
- wantStr: `("value")`,
- },
- {
- k: Key{[]byte(nil)},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{[]byte{}},
- wantProto: listValueProto(stringProto("")),
- wantStr: `("")`,
- },
- {
- k: Key{tm},
- wantProto: listValueProto(stringProto("2016-11-15T15:04:05.999999999Z")),
- wantStr: `("2016-11-15T15:04:05.999999999Z")`,
- },
- {k: Key{dt},
- wantProto: listValueProto(stringProto("2016-11-15")),
- wantStr: `("2016-11-15")`,
- },
- {
- k: Key{[]byte("value")},
- wantProto: listValueProto(bytesProto([]byte("value"))),
- wantStr: `("value")`,
- },
- {
- k: Key{NullInt64{1, true}},
- wantProto: listValueProto(stringProto("1")),
- wantStr: "(1)",
- },
- {
- k: Key{NullInt64{2, false}},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{NullFloat64{1.5, true}},
- wantProto: listValueProto(floatProto(1.5)),
- wantStr: "(1.5)",
- },
- {
- k: Key{NullFloat64{2.0, false}},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{NullBool{true, true}},
- wantProto: listValueProto(boolProto(true)),
- wantStr: "(true)",
- },
- {
- k: Key{NullBool{true, false}},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{NullString{"value", true}},
- wantProto: listValueProto(stringProto("value")),
- wantStr: `("value")`,
- },
- {
- k: Key{NullString{"value", false}},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{NullTime{tm, true}},
- wantProto: listValueProto(timeProto(tm)),
- wantStr: `("2016-11-15T15:04:05.999999999Z")`,
- },
-
- {
- k: Key{NullTime{time.Now(), false}},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{NullDate{dt, true}},
- wantProto: listValueProto(dateProto(dt)),
- wantStr: `("2016-11-15")`,
- },
- {
- k: Key{NullDate{civil.Date{}, false}},
- wantProto: listValueProto(nullProto()),
- wantStr: "(<null>)",
- },
- {
- k: Key{int(1), NullString{"value", false}, "value", 1.5, true},
- wantProto: listValueProto(stringProto("1"), nullProto(), stringProto("value"), floatProto(1.5), boolProto(true)),
- wantStr: `(1,<null>,"value",1.5,true)`,
- },
- } {
- if got := test.k.String(); got != test.wantStr {
- t.Errorf("%v.String() = %v, want %v", test.k, got, test.wantStr)
- }
- gotProto, err := test.k.proto()
- if err != nil {
- t.Errorf("%v.proto() returns error %v; want nil error", test.k, err)
- }
- if !reflect.DeepEqual(gotProto, test.wantProto) {
- t.Errorf("%v.proto() = \n%v\nwant:\n%v", test.k, gotProto, test.wantProto)
- }
- }
-}
-
-// Test KeyRange.String() and KeyRange.proto().
-func TestKeyRange(t *testing.T) {
- for _, test := range []struct {
- kr KeyRange
- wantProto *sppb.KeyRange
- wantStr string
- }{
- {
- kr: KeyRange{Key{"A"}, Key{"D"}, OpenOpen},
- wantProto: &sppb.KeyRange{
- &sppb.KeyRange_StartOpen{listValueProto(stringProto("A"))},
- &sppb.KeyRange_EndOpen{listValueProto(stringProto("D"))},
- },
- wantStr: `(("A"),("D"))`,
- },
- {
- kr: KeyRange{Key{1}, Key{10}, OpenClosed},
- wantProto: &sppb.KeyRange{
- &sppb.KeyRange_StartOpen{listValueProto(stringProto("1"))},
- &sppb.KeyRange_EndClosed{listValueProto(stringProto("10"))},
- },
- wantStr: "((1),(10)]",
- },
- {
- kr: KeyRange{Key{1.5, 2.1, 0.2}, Key{1.9, 0.7}, ClosedOpen},
- wantProto: &sppb.KeyRange{
- &sppb.KeyRange_StartClosed{listValueProto(floatProto(1.5), floatProto(2.1), floatProto(0.2))},
- &sppb.KeyRange_EndOpen{listValueProto(floatProto(1.9), floatProto(0.7))},
- },
- wantStr: "[(1.5,2.1,0.2),(1.9,0.7))",
- },
- {
- kr: KeyRange{Key{NullInt64{1, true}}, Key{10}, ClosedClosed},
- wantProto: &sppb.KeyRange{
- &sppb.KeyRange_StartClosed{listValueProto(stringProto("1"))},
- &sppb.KeyRange_EndClosed{listValueProto(stringProto("10"))},
- },
- wantStr: "[(1),(10)]",
- },
- } {
- if got := test.kr.String(); got != test.wantStr {
- t.Errorf("%v.String() = %v, want %v", test.kr, got, test.wantStr)
- }
- gotProto, err := test.kr.proto()
- if err != nil {
- t.Errorf("%v.proto() returns error %v; want nil error", test.kr, err)
- }
- if !reflect.DeepEqual(gotProto, test.wantProto) {
- t.Errorf("%v.proto() = \n%v\nwant:\n%v", test.kr, gotProto.String(), test.wantProto.String())
- }
- }
-}
-
-func TestPrefixRange(t *testing.T) {
- got := Key{1}.AsPrefix()
- want := KeyRange{Start: Key{1}, End: Key{1}, Kind: ClosedClosed}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("got %v, want %v", got, want)
- }
-}
-
-func TestKeySets(t *testing.T) {
- int1 := intProto(1)
- int2 := intProto(2)
- int3 := intProto(3)
- int4 := intProto(4)
- for i, test := range []struct {
- ks KeySet
- wantProto *sppb.KeySet
- }{
- {
- KeySets(),
- &sppb.KeySet{},
- },
- {
- Key{4},
- &sppb.KeySet{
- Keys: []*proto3.ListValue{listValueProto(int4)},
- },
- },
- {
- AllKeys(),
- &sppb.KeySet{All: true},
- },
- {
- KeySets(Key{1, 2}, Key{3, 4}),
- &sppb.KeySet{
- Keys: []*proto3.ListValue{
- listValueProto(int1, int2),
- listValueProto(int3, int4),
- },
- },
- },
- {
- KeyRange{Key{1}, Key{2}, ClosedOpen},
- &sppb.KeySet{Ranges: []*sppb.KeyRange{
- &sppb.KeyRange{
- &sppb.KeyRange_StartClosed{listValueProto(int1)},
- &sppb.KeyRange_EndOpen{listValueProto(int2)},
- },
- }},
- },
- {
- Key{2}.AsPrefix(),
- &sppb.KeySet{Ranges: []*sppb.KeyRange{
- &sppb.KeyRange{
- &sppb.KeyRange_StartClosed{listValueProto(int2)},
- &sppb.KeyRange_EndClosed{listValueProto(int2)},
- },
- }},
- },
- {
- KeySets(
- KeyRange{Key{1}, Key{2}, ClosedClosed},
- KeyRange{Key{3}, Key{4}, OpenClosed},
- ),
- &sppb.KeySet{
- Ranges: []*sppb.KeyRange{
- &sppb.KeyRange{
- &sppb.KeyRange_StartClosed{listValueProto(int1)},
- &sppb.KeyRange_EndClosed{listValueProto(int2)},
- },
- &sppb.KeyRange{
- &sppb.KeyRange_StartOpen{listValueProto(int3)},
- &sppb.KeyRange_EndClosed{listValueProto(int4)},
- },
- },
- },
- },
- {
- KeySets(
- Key{1},
- KeyRange{Key{2}, Key{3}, ClosedClosed},
- KeyRange{Key{4}, Key{5}, OpenClosed},
- KeySets(),
- Key{6}),
- &sppb.KeySet{
- Keys: []*proto3.ListValue{
- listValueProto(int1),
- listValueProto(intProto(6)),
- },
- Ranges: []*sppb.KeyRange{
- &sppb.KeyRange{
- &sppb.KeyRange_StartClosed{listValueProto(int2)},
- &sppb.KeyRange_EndClosed{listValueProto(int3)},
- },
- &sppb.KeyRange{
- &sppb.KeyRange_StartOpen{listValueProto(int4)},
- &sppb.KeyRange_EndClosed{listValueProto(intProto(5))},
- },
- },
- },
- },
- {
- KeySets(
- Key{1},
- KeyRange{Key{2}, Key{3}, ClosedClosed},
- AllKeys(),
- KeyRange{Key{4}, Key{5}, OpenClosed},
- Key{6}),
- &sppb.KeySet{All: true},
- },
- } {
- gotProto, err := test.ks.keySetProto()
- if err != nil {
- t.Errorf("#%d: %v.proto() returns error %v; want nil error", i, test.ks, err)
- }
- if !reflect.DeepEqual(gotProto, test.wantProto) {
- t.Errorf("#%d: %v.proto() = \n%v\nwant:\n%v", i, test.ks, gotProto.String(), test.wantProto.String())
- }
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/mutation.go b/vendor/cloud.google.com/go/spanner/mutation.go
deleted file mode 100644
index 81f25746d..000000000
--- a/vendor/cloud.google.com/go/spanner/mutation.go
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "reflect"
-
- proto3 "github.com/golang/protobuf/ptypes/struct"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc/codes"
-)
-
-// op is the mutation operation.
-type op int
-
-const (
- // opDelete removes a row from a table. Succeeds whether or not the
- // key was present.
- opDelete op = iota
- // opInsert inserts a row into a table. If the row already exists, the
- // write or transaction fails.
- opInsert
- // opInsertOrUpdate inserts a row into a table. If the row already
- // exists, it updates it instead. Any column values not explicitly
- // written are preserved.
- opInsertOrUpdate
- // opReplace inserts a row into a table, deleting any existing row.
- // Unlike InsertOrUpdate, this means any values not explicitly written
- // become NULL.
- opReplace
- // opUpdate updates a row in a table. If the row does not already
- // exist, the write or transaction fails.
- opUpdate
-)
-
-// A Mutation describes a modification to one or more Cloud Spanner rows. The
-// mutation represents an insert, update, delete, etc on a table.
-//
-// Many mutations can be applied in a single atomic commit. For purposes of
-// constraint checking (such as foreign key constraints), the operations can be
-// viewed as applying in same order as the mutations are supplied in (so that
-// e.g., a row and its logical "child" can be inserted in the same commit).
-//
-// - The Apply function applies series of mutations.
-// - A ReadWriteTransaction applies a series of mutations as part of an
-// atomic read-modify-write operation.
-// Example:
-//
-// m := spanner.Insert("User",
-// []string{"user_id", "profile"},
-// []interface{}{UserID, profile})
-// _, err := client.Apply(ctx, []*spanner.Mutation{m})
-//
-// In this example, we insert a new row into the User table. The primary key
-// for the new row is UserID (presuming that "user_id" has been declared as the
-// primary key of the "User" table).
-//
-// Updating a row
-//
-// Changing the values of columns in an existing row is very similar to
-// inserting a new row:
-//
-// m := spanner.Update("User",
-// []string{"user_id", "profile"},
-// []interface{}{UserID, profile})
-// _, err := client.Apply(ctx, []*spanner.Mutation{m})
-//
-// Deleting a row
-//
-// To delete a row, use spanner.Delete:
-//
-// m := spanner.Delete("User", spanner.Key{UserId})
-// _, err := client.Apply(ctx, []*spanner.Mutation{m})
-//
-// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use the
-// spanner.KeySets function to build any combination of Keys and KeyRanges.
-//
-// Note that deleting a row in a table may also delete rows from other tables
-// if cascading deletes are specified in those tables' schemas. Delete does
-// nothing if the named row does not exist (does not yield an error).
-//
-// Deleting a field
-//
-// To delete/clear a field within a row, use spanner.Update with the value nil:
-//
-// m := spanner.Update("User",
-// []string{"user_id", "profile"},
-// []interface{}{UserID, nil})
-// _, err := client.Apply(ctx, []*spanner.Mutation{m})
-//
-// The valid Go types and their corresponding Cloud Spanner types that can be
-// used in the Insert/Update/InsertOrUpdate functions are:
-//
-// string, NullString - STRING
-// []string, []NullString - STRING ARRAY
-// []byte - BYTES
-// [][]byte - BYTES ARRAY
-// int, int64, NullInt64 - INT64
-// []int, []int64, []NullInt64 - INT64 ARRAY
-// bool, NullBool - BOOL
-// []bool, []NullBool - BOOL ARRAY
-// float64, NullFloat64 - FLOAT64
-// []float64, []NullFloat64 - FLOAT64 ARRAY
-// time.Time, NullTime - TIMESTAMP
-// []time.Time, []NullTime - TIMESTAMP ARRAY
-// Date, NullDate - DATE
-// []Date, []NullDate - DATE ARRAY
-//
-// To compare two Mutations for testing purposes, use reflect.DeepEqual.
-type Mutation struct {
- // op is the operation type of the mutation.
- // See documentation for spanner.op for more details.
- op op
- // Table is the name of the taget table to be modified.
- table string
- // keySet is a set of primary keys that names the rows
- // in a delete operation.
- keySet KeySet
- // columns names the set of columns that are going to be
- // modified by Insert, InsertOrUpdate, Replace or Update
- // operations.
- columns []string
- // values specifies the new values for the target columns
- // named by Columns.
- values []interface{}
-}
-
-// mapToMutationParams converts Go map into mutation parameters.
-func mapToMutationParams(in map[string]interface{}) ([]string, []interface{}) {
- cols := []string{}
- vals := []interface{}{}
- for k, v := range in {
- cols = append(cols, k)
- vals = append(vals, v)
- }
- return cols, vals
-}
-
-// errNotStruct returns error for not getting a go struct type.
-func errNotStruct(in interface{}) error {
- return spannerErrorf(codes.InvalidArgument, "%T is not a go struct type", in)
-}
-
-// structToMutationParams converts Go struct into mutation parameters.
-// If the input is not a valid Go struct type, structToMutationParams
-// returns error.
-func structToMutationParams(in interface{}) ([]string, []interface{}, error) {
- if in == nil {
- return nil, nil, errNotStruct(in)
- }
- v := reflect.ValueOf(in)
- t := v.Type()
- if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
- // t is a pointer to a struct.
- if v.IsNil() {
- // Return empty results.
- return nil, nil, nil
- }
- // Get the struct value that in points to.
- v = v.Elem()
- t = t.Elem()
- }
- if t.Kind() != reflect.Struct {
- return nil, nil, errNotStruct(in)
- }
- fields, err := fieldCache.Fields(t)
- if err != nil {
- return nil, nil, toSpannerError(err)
- }
- var cols []string
- var vals []interface{}
- for _, f := range fields {
- cols = append(cols, f.Name)
- vals = append(vals, v.FieldByIndex(f.Index).Interface())
- }
- return cols, vals, nil
-}
-
-// Insert returns a Mutation to insert a row into a table. If the row already
-// exists, the write or transaction fails.
-func Insert(table string, cols []string, vals []interface{}) *Mutation {
- return &Mutation{
- op: opInsert,
- table: table,
- columns: cols,
- values: vals,
- }
-}
-
-// InsertMap returns a Mutation to insert a row into a table, specified by
-// a map of column name to value. If the row already exists, the write or
-// transaction fails.
-func InsertMap(table string, in map[string]interface{}) *Mutation {
- cols, vals := mapToMutationParams(in)
- return Insert(table, cols, vals)
-}
-
-// InsertStruct returns a Mutation to insert a row into a table, specified by
-// a Go struct. If the row already exists, the write or transaction fails.
-//
-// The in argument must be a struct or a pointer to a struct. Its exported
-// fields specify the column names and values. Use a field tag like "spanner:name"
-// to provide an alternative column name, or use "spanner:-" to ignore the field.
-func InsertStruct(table string, in interface{}) (*Mutation, error) {
- cols, vals, err := structToMutationParams(in)
- if err != nil {
- return nil, err
- }
- return Insert(table, cols, vals), nil
-}
-
-// Update returns a Mutation to update a row in a table. If the row does not
-// already exist, the write or transaction fails.
-func Update(table string, cols []string, vals []interface{}) *Mutation {
- return &Mutation{
- op: opUpdate,
- table: table,
- columns: cols,
- values: vals,
- }
-}
-
-// UpdateMap returns a Mutation to update a row in a table, specified by
-// a map of column to value. If the row does not already exist, the write or
-// transaction fails.
-func UpdateMap(table string, in map[string]interface{}) *Mutation {
- cols, vals := mapToMutationParams(in)
- return Update(table, cols, vals)
-}
-
-// UpdateStruct returns a Mutation to update a row in a table, specified by a Go
-// struct. If the row does not already exist, the write or transaction fails.
-func UpdateStruct(table string, in interface{}) (*Mutation, error) {
- cols, vals, err := structToMutationParams(in)
- if err != nil {
- return nil, err
- }
- return Update(table, cols, vals), nil
-}
-
-// InsertOrUpdate returns a Mutation to insert a row into a table. If the row
-// already exists, it updates it instead. Any column values not explicitly
-// written are preserved.
-//
-// For a similar example, See Update.
-func InsertOrUpdate(table string, cols []string, vals []interface{}) *Mutation {
- return &Mutation{
- op: opInsertOrUpdate,
- table: table,
- columns: cols,
- values: vals,
- }
-}
-
-// InsertOrUpdateMap returns a Mutation to insert a row into a table,
-// specified by a map of column to value. If the row already exists, it
-// updates it instead. Any column values not explicitly written are preserved.
-//
-// For a similar example, See UpdateMap.
-func InsertOrUpdateMap(table string, in map[string]interface{}) *Mutation {
- cols, vals := mapToMutationParams(in)
- return InsertOrUpdate(table, cols, vals)
-}
-
-// InsertOrUpdateStruct returns a Mutation to insert a row into a table,
-// specified by a Go struct. If the row already exists, it updates it instead.
-// Any column values not explicitly written are preserved.
-//
-// The in argument must be a struct or a pointer to a struct. Its exported
-// fields specify the column names and values. Use a field tag like "spanner:name"
-// to provide an alternative column name, or use "spanner:-" to ignore the field.
-//
-// For a similar example, See UpdateStruct.
-func InsertOrUpdateStruct(table string, in interface{}) (*Mutation, error) {
- cols, vals, err := structToMutationParams(in)
- if err != nil {
- return nil, err
- }
- return InsertOrUpdate(table, cols, vals), nil
-}
-
-// Replace returns a Mutation to insert a row into a table, deleting any
-// existing row. Unlike InsertOrUpdate, this means any values not explicitly
-// written become NULL.
-//
-// For a similar example, See Update.
-func Replace(table string, cols []string, vals []interface{}) *Mutation {
- return &Mutation{
- op: opReplace,
- table: table,
- columns: cols,
- values: vals,
- }
-}
-
-// ReplaceMap returns a Mutation to insert a row into a table, deleting any
-// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
-// written become NULL. The row is specified by a map of column to value.
-//
-// For a similar example, See UpdateMap.
-func ReplaceMap(table string, in map[string]interface{}) *Mutation {
- cols, vals := mapToMutationParams(in)
- return Replace(table, cols, vals)
-}
-
-// ReplaceStruct returns a Mutation to insert a row into a table, deleting any
-// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
-// written become NULL. The row is specified by a Go struct.
-//
-// The in argument must be a struct or a pointer to a struct. Its exported
-// fields specify the column names and values. Use a field tag like "spanner:name"
-// to provide an alternative column name, or use "spanner:-" to ignore the field.
-//
-// For a similar example, See UpdateStruct.
-func ReplaceStruct(table string, in interface{}) (*Mutation, error) {
- cols, vals, err := structToMutationParams(in)
- if err != nil {
- return nil, err
- }
- return Replace(table, cols, vals), nil
-}
-
-// Delete removes the rows described by the KeySet from the table. It succeeds
-// whether or not the keys were present.
-func Delete(table string, ks KeySet) *Mutation {
- return &Mutation{
- op: opDelete,
- table: table,
- keySet: ks,
- }
-}
-
-// prepareWrite generates sppb.Mutation_Write from table name, column names
-// and new column values.
-func prepareWrite(table string, columns []string, vals []interface{}) (*sppb.Mutation_Write, error) {
- v, err := encodeValueArray(vals)
- if err != nil {
- return nil, err
- }
- return &sppb.Mutation_Write{
- Table: table,
- Columns: columns,
- Values: []*proto3.ListValue{v},
- }, nil
-}
-
-// errInvdMutationOp returns error for unrecognized mutation operation.
-func errInvdMutationOp(m Mutation) error {
- return spannerErrorf(codes.InvalidArgument, "Unknown op type: %d", m.op)
-}
-
-// proto converts spanner.Mutation to sppb.Mutation, in preparation to send
-// RPCs.
-func (m Mutation) proto() (*sppb.Mutation, error) {
- var pb *sppb.Mutation
- switch m.op {
- case opDelete:
- var kp *sppb.KeySet
- if m.keySet != nil {
- var err error
- kp, err = m.keySet.keySetProto()
- if err != nil {
- return nil, err
- }
- }
- pb = &sppb.Mutation{
- Operation: &sppb.Mutation_Delete_{
- Delete: &sppb.Mutation_Delete{
- Table: m.table,
- KeySet: kp,
- },
- },
- }
- case opInsert:
- w, err := prepareWrite(m.table, m.columns, m.values)
- if err != nil {
- return nil, err
- }
- pb = &sppb.Mutation{Operation: &sppb.Mutation_Insert{Insert: w}}
- case opInsertOrUpdate:
- w, err := prepareWrite(m.table, m.columns, m.values)
- if err != nil {
- return nil, err
- }
- pb = &sppb.Mutation{Operation: &sppb.Mutation_InsertOrUpdate{InsertOrUpdate: w}}
- case opReplace:
- w, err := prepareWrite(m.table, m.columns, m.values)
- if err != nil {
- return nil, err
- }
- pb = &sppb.Mutation{Operation: &sppb.Mutation_Replace{Replace: w}}
- case opUpdate:
- w, err := prepareWrite(m.table, m.columns, m.values)
- if err != nil {
- return nil, err
- }
- pb = &sppb.Mutation{Operation: &sppb.Mutation_Update{Update: w}}
- default:
- return nil, errInvdMutationOp(m)
- }
- return pb, nil
-}
-
-// mutationsProto turns a spanner.Mutation array into a sppb.Mutation array,
-// it is convenient for sending batch mutations to Cloud Spanner.
-func mutationsProto(ms []*Mutation) ([]*sppb.Mutation, error) {
- l := make([]*sppb.Mutation, 0, len(ms))
- for _, m := range ms {
- pb, err := m.proto()
- if err != nil {
- return nil, err
- }
- l = append(l, pb)
- }
- return l, nil
-}
diff --git a/vendor/cloud.google.com/go/spanner/mutation_test.go b/vendor/cloud.google.com/go/spanner/mutation_test.go
deleted file mode 100644
index 795db7c89..000000000
--- a/vendor/cloud.google.com/go/spanner/mutation_test.go
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "reflect"
- "sort"
- "strings"
- "testing"
-
- proto3 "github.com/golang/protobuf/ptypes/struct"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// keysetProto returns protobuf encoding of valid spanner.KeySet.
-func keysetProto(t *testing.T, ks KeySet) *sppb.KeySet {
- k, err := ks.keySetProto()
- if err != nil {
- t.Fatalf("cannot convert keyset %v to protobuf: %v", ks, err)
- }
- return k
-}
-
-// Test encoding from spanner.Mutation to protobuf.
-func TestMutationToProto(t *testing.T) {
- for i, test := range []struct {
- m *Mutation
- want *sppb.Mutation
- }{
- // Delete Mutation
- {
- &Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Delete_{
- Delete: &sppb.Mutation_Delete{
- Table: "t_foo",
- KeySet: keysetProto(t, Key{"foo"}),
- },
- },
- },
- },
- // Insert Mutation
- {
- &Mutation{opInsert, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Insert{
- Insert: &sppb.Mutation_Write{
- Table: "t_foo",
- Columns: []string{"col1", "col2"},
- Values: []*proto3.ListValue{
- &proto3.ListValue{
- Values: []*proto3.Value{intProto(1), intProto(2)},
- },
- },
- },
- },
- },
- },
- // InsertOrUpdate Mutation
- {
- &Mutation{opInsertOrUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_InsertOrUpdate{
- InsertOrUpdate: &sppb.Mutation_Write{
- Table: "t_foo",
- Columns: []string{"col1", "col2"},
- Values: []*proto3.ListValue{
- &proto3.ListValue{
- Values: []*proto3.Value{floatProto(1.0), floatProto(2.0)},
- },
- },
- },
- },
- },
- },
- // Replace Mutation
- {
- &Mutation{opReplace, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", 2.0}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Replace{
- Replace: &sppb.Mutation_Write{
- Table: "t_foo",
- Columns: []string{"col1", "col2"},
- Values: []*proto3.ListValue{
- &proto3.ListValue{
- Values: []*proto3.Value{stringProto("one"), floatProto(2.0)},
- },
- },
- },
- },
- },
- },
- // Update Mutation
- {
- &Mutation{opUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Update{
- Update: &sppb.Mutation_Write{
- Table: "t_foo",
- Columns: []string{"col1", "col2"},
- Values: []*proto3.ListValue{
- &proto3.ListValue{
- Values: []*proto3.Value{stringProto("one"), nullProto()},
- },
- },
- },
- },
- },
- },
- } {
- if got, err := test.m.proto(); err != nil || !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: (%#v).proto() = (%v, %v), want (%v, nil)", i, test.m, got, err, test.want)
- }
- }
-}
-
-// mutationColumnSorter implements sort.Interface for sorting column-value pairs in a Mutation by column names.
-type mutationColumnSorter struct {
- Mutation
-}
-
-// newMutationColumnSorter creates new instance of mutationColumnSorter by duplicating the input Mutation so that
-// sorting won't change the input Mutation.
-func newMutationColumnSorter(m *Mutation) *mutationColumnSorter {
- return &mutationColumnSorter{
- Mutation{
- m.op,
- m.table,
- m.keySet,
- append([]string(nil), m.columns...),
- append([]interface{}(nil), m.values...),
- },
- }
-}
-
-// Len implements sort.Interface.Len.
-func (ms *mutationColumnSorter) Len() int {
- return len(ms.columns)
-}
-
-// Swap implements sort.Interface.Swap.
-func (ms *mutationColumnSorter) Swap(i, j int) {
- ms.columns[i], ms.columns[j] = ms.columns[j], ms.columns[i]
- ms.values[i], ms.values[j] = ms.values[j], ms.values[i]
-}
-
-// Less implements sort.Interface.Less.
-func (ms *mutationColumnSorter) Less(i, j int) bool {
- return strings.Compare(ms.columns[i], ms.columns[j]) < 0
-}
-
-// mutationEqual returns true if two mutations in question are equal
-// to each other.
-func mutationEqual(t *testing.T, m1, m2 Mutation) bool {
- // Two mutations are considered to be equal even if their column values have different
- // orders.
- ms1 := newMutationColumnSorter(&m1)
- ms2 := newMutationColumnSorter(&m2)
- sort.Sort(ms1)
- sort.Sort(ms2)
- return reflect.DeepEqual(ms1, ms2)
-}
-
-// Test helper functions which help to generate spanner.Mutation.
-func TestMutationHelpers(t *testing.T) {
- for _, test := range []struct {
- m string
- got *Mutation
- want *Mutation
- }{
- {
- "Insert",
- Insert("t_foo", []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}),
- &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
- },
- {
- "InsertMap",
- InsertMap("t_foo", map[string]interface{}{"col1": int64(1), "col2": int64(2)}),
- &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
- },
- {
- "InsertStruct",
- func() *Mutation {
- m, err := InsertStruct(
- "t_foo",
- struct {
- notCol bool
- Col1 int64 `spanner:"col1"`
- Col2 int64 `spanner:"col2"`
- }{false, int64(1), int64(2)},
- )
- if err != nil {
- t.Errorf("cannot convert struct into mutation: %v", err)
- }
- return m
- }(),
- &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
- },
- {
- "Update",
- Update("t_foo", []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}),
- &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
- },
- {
- "UpdateMap",
- UpdateMap("t_foo", map[string]interface{}{"col1": "one", "col2": []byte(nil)}),
- &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
- },
- {
- "UpdateStruct",
- func() *Mutation {
- m, err := UpdateStruct(
- "t_foo",
- struct {
- Col1 string `spanner:"col1"`
- notCol int
- Col2 []byte `spanner:"col2"`
- }{"one", 1, nil},
- )
- if err != nil {
- t.Errorf("cannot convert struct into mutation: %v", err)
- }
- return m
- }(),
- &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
- },
- {
- "InsertOrUpdate",
- InsertOrUpdate("t_foo", []string{"col1", "col2"}, []interface{}{1.0, 2.0}),
- &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
- },
- {
- "InsertOrUpdateMap",
- InsertOrUpdateMap("t_foo", map[string]interface{}{"col1": 1.0, "col2": 2.0}),
- &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
- },
- {
- "InsertOrUpdateStruct",
- func() *Mutation {
- m, err := InsertOrUpdateStruct(
- "t_foo",
- struct {
- Col1 float64 `spanner:"col1"`
- Col2 float64 `spanner:"col2"`
- notCol float64
- }{1.0, 2.0, 3.0},
- )
- if err != nil {
- t.Errorf("cannot convert struct into mutation: %v", err)
- }
- return m
- }(),
- &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
- },
- {
- "Replace",
- Replace("t_foo", []string{"col1", "col2"}, []interface{}{"one", 2.0}),
- &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}},
- },
- {
- "ReplaceMap",
- ReplaceMap("t_foo", map[string]interface{}{"col1": "one", "col2": 2.0}),
- &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}},
- },
- {
- "ReplaceStruct",
- func() *Mutation {
- m, err := ReplaceStruct(
- "t_foo",
- struct {
- Col1 string `spanner:"col1"`
- Col2 float64 `spanner:"col2"`
- notCol string
- }{"one", 2.0, "foo"},
- )
- if err != nil {
- t.Errorf("cannot convert struct into mutation: %v", err)
- }
- return m
- }(),
- &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}},
- },
- {
- "Delete",
- Delete("t_foo", Key{"foo"}),
- &Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil},
- },
- {
- "DeleteRange",
- Delete("t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}),
- &Mutation{opDelete, "t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}, nil, nil},
- },
- } {
- if !mutationEqual(t, *test.got, *test.want) {
- t.Errorf("%v: got Mutation %v, want %v", test.m, test.got, test.want)
- }
- }
-}
-
-// Test encoding non-struct types by using *Struct helpers.
-func TestBadStructs(t *testing.T) {
- val := "i_am_not_a_struct"
- wantErr := errNotStruct(val)
- if _, gotErr := InsertStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("InsertStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
- }
- if _, gotErr := InsertOrUpdateStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("InsertOrUpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
- }
- if _, gotErr := UpdateStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("UpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
- }
- if _, gotErr := ReplaceStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("ReplaceStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
- }
-}
-
-// Test encoding Mutation into proto.
-func TestEncodeMutation(t *testing.T) {
- for _, test := range []struct {
- name string
- mutation Mutation
- wantProto *sppb.Mutation
- wantErr error
- }{
- {
- "OpDelete",
- Mutation{opDelete, "t_test", Key{1}, nil, nil},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Delete_{
- Delete: &sppb.Mutation_Delete{
- Table: "t_test",
- KeySet: &sppb.KeySet{
- Keys: []*proto3.ListValue{listValueProto(intProto(1))},
- },
- },
- },
- },
- nil,
- },
- {
- "OpDelete - Key error",
- Mutation{opDelete, "t_test", Key{struct{}{}}, nil, nil},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Delete_{
- Delete: &sppb.Mutation_Delete{
- Table: "t_test",
- KeySet: &sppb.KeySet{},
- },
- },
- },
- errInvdKeyPartType(struct{}{}),
- },
- {
- "OpInsert",
- Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Insert{
- Insert: &sppb.Mutation_Write{
- Table: "t_test",
- Columns: []string{"key", "val"},
- Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
- },
- },
- },
- nil,
- },
- {
- "OpInsert - Value Type Error",
- Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Insert{
- Insert: &sppb.Mutation_Write{},
- },
- },
- errEncoderUnsupportedType(struct{}{}),
- },
- {
- "OpInsertOrUpdate",
- Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_InsertOrUpdate{
- InsertOrUpdate: &sppb.Mutation_Write{
- Table: "t_test",
- Columns: []string{"key", "val"},
- Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
- },
- },
- },
- nil,
- },
- {
- "OpInsertOrUpdate - Value Type Error",
- Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_InsertOrUpdate{
- InsertOrUpdate: &sppb.Mutation_Write{},
- },
- },
- errEncoderUnsupportedType(struct{}{}),
- },
- {
- "OpReplace",
- Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Replace{
- Replace: &sppb.Mutation_Write{
- Table: "t_test",
- Columns: []string{"key", "val"},
- Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
- },
- },
- },
- nil,
- },
- {
- "OpReplace - Value Type Error",
- Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Replace{
- Replace: &sppb.Mutation_Write{},
- },
- },
- errEncoderUnsupportedType(struct{}{}),
- },
- {
- "OpUpdate",
- Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Update{
- Update: &sppb.Mutation_Write{
- Table: "t_test",
- Columns: []string{"key", "val"},
- Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
- },
- },
- },
- nil,
- },
- {
- "OpUpdate - Value Type Error",
- Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
- &sppb.Mutation{
- Operation: &sppb.Mutation_Update{
- Update: &sppb.Mutation_Write{},
- },
- },
- errEncoderUnsupportedType(struct{}{}),
- },
- {
- "OpKnown - Unknown Mutation Operation Code",
- Mutation{op(100), "t_test", nil, nil, nil},
- &sppb.Mutation{},
- errInvdMutationOp(Mutation{op(100), "t_test", nil, nil, nil}),
- },
- } {
- gotProto, gotErr := test.mutation.proto()
- if gotErr != nil {
- if !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%s: %v.proto() returns error %v, want %v", test.name, test.mutation, gotErr, test.wantErr)
- }
- continue
- }
- if !reflect.DeepEqual(gotProto, test.wantProto) {
- t.Errorf("%s: %v.proto() = (%v, nil), want (%v, nil)", test.name, test.mutation, gotProto, test.wantProto)
- }
- }
-}
-
-// Test Encoding an array of mutations.
-func TestEncodeMutationArray(t *testing.T) {
- for _, test := range []struct {
- name string
- ms []*Mutation
- want []*sppb.Mutation
- wantErr error
- }{
- {
- "Multiple Mutations",
- []*Mutation{
- &Mutation{opDelete, "t_test", Key{"bar"}, nil, nil},
- &Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
- },
- []*sppb.Mutation{
- &sppb.Mutation{
- Operation: &sppb.Mutation_Delete_{
- Delete: &sppb.Mutation_Delete{
- Table: "t_test",
- KeySet: &sppb.KeySet{
- Keys: []*proto3.ListValue{listValueProto(stringProto("bar"))},
- },
- },
- },
- },
- &sppb.Mutation{
- Operation: &sppb.Mutation_InsertOrUpdate{
- InsertOrUpdate: &sppb.Mutation_Write{
- Table: "t_test",
- Columns: []string{"key", "val"},
- Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
- },
- },
- },
- },
- nil,
- },
- {
- "Multiple Mutations - Bad Mutation",
- []*Mutation{
- &Mutation{opDelete, "t_test", Key{"bar"}, nil, nil},
- &Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", struct{}{}}},
- },
- []*sppb.Mutation{},
- errEncoderUnsupportedType(struct{}{}),
- },
- } {
- gotProto, gotErr := mutationsProto(test.ms)
- if gotErr != nil {
- if !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: mutationsProto(%v) returns error %v, want %v", test.name, test.ms, gotErr, test.wantErr)
- }
- continue
- }
- if !reflect.DeepEqual(gotProto, test.want) {
- t.Errorf("%v: mutationsProto(%v) = (%v, nil), want (%v, nil)", test.name, test.ms, gotProto, test.want)
- }
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/protoutils.go b/vendor/cloud.google.com/go/spanner/protoutils.go
deleted file mode 100644
index df12432d5..000000000
--- a/vendor/cloud.google.com/go/spanner/protoutils.go
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "encoding/base64"
- "strconv"
- "time"
-
- "cloud.google.com/go/civil"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// Helpers to generate protobuf values and Cloud Spanner types.
-
-func stringProto(s string) *proto3.Value {
- return &proto3.Value{Kind: stringKind(s)}
-}
-
-func stringKind(s string) *proto3.Value_StringValue {
- return &proto3.Value_StringValue{StringValue: s}
-}
-
-func stringType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_STRING}
-}
-
-func boolProto(b bool) *proto3.Value {
- return &proto3.Value{Kind: &proto3.Value_BoolValue{BoolValue: b}}
-}
-
-func boolType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_BOOL}
-}
-
-func intProto(n int64) *proto3.Value {
- return &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: strconv.FormatInt(n, 10)}}
-}
-
-func intType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_INT64}
-}
-
-func floatProto(n float64) *proto3.Value {
- return &proto3.Value{Kind: &proto3.Value_NumberValue{NumberValue: n}}
-}
-
-func floatType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_FLOAT64}
-}
-
-func bytesProto(b []byte) *proto3.Value {
- return &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: base64.StdEncoding.EncodeToString(b)}}
-}
-
-func bytesType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_BYTES}
-}
-
-func timeProto(t time.Time) *proto3.Value {
- return stringProto(t.UTC().Format(time.RFC3339Nano))
-}
-
-func timeType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_TIMESTAMP}
-}
-
-func dateProto(d civil.Date) *proto3.Value {
- return stringProto(d.String())
-}
-
-func dateType() *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_DATE}
-}
-
-func listProto(p ...*proto3.Value) *proto3.Value {
- return &proto3.Value{Kind: &proto3.Value_ListValue{ListValue: &proto3.ListValue{Values: p}}}
-}
-
-func listValueProto(p ...*proto3.Value) *proto3.ListValue {
- return &proto3.ListValue{Values: p}
-}
-
-func listType(t *sppb.Type) *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_ARRAY, ArrayElementType: t}
-}
-
-func mkField(n string, t *sppb.Type) *sppb.StructType_Field {
- return &sppb.StructType_Field{n, t}
-}
-
-func structType(fields ...*sppb.StructType_Field) *sppb.Type {
- return &sppb.Type{Code: sppb.TypeCode_STRUCT, StructType: &sppb.StructType{Fields: fields}}
-}
-
-func nullProto() *proto3.Value {
- return &proto3.Value{Kind: &proto3.Value_NullValue{NullValue: proto3.NullValue_NULL_VALUE}}
-}
diff --git a/vendor/cloud.google.com/go/spanner/read.go b/vendor/cloud.google.com/go/spanner/read.go
deleted file mode 100644
index 5d733f1ca..000000000
--- a/vendor/cloud.google.com/go/spanner/read.go
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "bytes"
- "io"
- "sync/atomic"
- "time"
-
- log "github.com/golang/glog"
- proto "github.com/golang/protobuf/proto"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- "golang.org/x/net/context"
-
- "google.golang.org/api/iterator"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc/codes"
-)
-
-// streamingReceiver is the interface for receiving data from a client side
-// stream.
-type streamingReceiver interface {
- Recv() (*sppb.PartialResultSet, error)
-}
-
-// errEarlyReadEnd returns error for read finishes when gRPC stream is still active.
-func errEarlyReadEnd() error {
- return spannerErrorf(codes.FailedPrecondition, "read completed with active stream")
-}
-
-// stream is the internal fault tolerant method for streaming data from
-// Cloud Spanner.
-func stream(ctx context.Context, rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error), setTimestamp func(time.Time), release func(error)) *RowIterator {
- ctx, cancel := context.WithCancel(ctx)
- return &RowIterator{
- streamd: newResumableStreamDecoder(ctx, rpc),
- rowd: &partialResultSetDecoder{},
- setTimestamp: setTimestamp,
- release: release,
- cancel: cancel,
- }
-}
-
-// RowIterator is an iterator over Rows.
-type RowIterator struct {
- streamd *resumableStreamDecoder
- rowd *partialResultSetDecoder
- setTimestamp func(time.Time)
- release func(error)
- cancel func()
- err error
- rows []*Row
-}
-
-// Next returns the next result. Its second return value is iterator.Done if
-// there are no more results. Once Next returns Done, all subsequent calls
-// will return Done.
-func (r *RowIterator) Next() (*Row, error) {
- if r.err != nil {
- return nil, r.err
- }
- for len(r.rows) == 0 && r.streamd.next() {
- r.rows, r.err = r.rowd.add(r.streamd.get())
- if r.err != nil {
- return nil, r.err
- }
- if !r.rowd.ts.IsZero() && r.setTimestamp != nil {
- r.setTimestamp(r.rowd.ts)
- r.setTimestamp = nil
- }
- }
- if len(r.rows) > 0 {
- row := r.rows[0]
- r.rows = r.rows[1:]
- return row, nil
- }
- if err := r.streamd.lastErr(); err != nil {
- r.err = toSpannerError(err)
- } else if !r.rowd.done() {
- r.err = errEarlyReadEnd()
- } else {
- r.err = iterator.Done
- }
- return nil, r.err
-}
-
-// Do calls the provided function once in sequence for each row in the iteration. If the
-// function returns a non-nil error, Do immediately returns that error.
-//
-// If there are no rows in the iterator, Do will return nil without calling the
-// provided function.
-//
-// Do always calls Stop on the iterator.
-func (r *RowIterator) Do(f func(r *Row) error) error {
- defer r.Stop()
- for {
- row, err := r.Next()
- switch err {
- case iterator.Done:
- return nil
- case nil:
- if err = f(row); err != nil {
- return err
- }
- default:
- return err
- }
- }
-}
-
-// Stop terminates the iteration. It should be called after every iteration.
-func (r *RowIterator) Stop() {
- if r.cancel != nil {
- r.cancel()
- }
- if r.release != nil {
- r.release(r.err)
- if r.err == nil {
- r.err = spannerErrorf(codes.FailedPrecondition, "Next called after Stop")
- }
- r.release = nil
-
- }
-}
-
-// partialResultQueue implements a simple FIFO queue. The zero value is a
-// valid queue.
-type partialResultQueue struct {
- q []*sppb.PartialResultSet
- first int
- last int
- n int // number of elements in queue
-}
-
-// empty returns if the partialResultQueue is empty.
-func (q *partialResultQueue) empty() bool {
- return q.n == 0
-}
-
-// errEmptyQueue returns error for dequeuing an empty queue.
-func errEmptyQueue() error {
- return spannerErrorf(codes.OutOfRange, "empty partialResultQueue")
-}
-
-// peekLast returns the last item in partialResultQueue; if the queue
-// is empty, it returns error.
-func (q *partialResultQueue) peekLast() (*sppb.PartialResultSet, error) {
- if q.empty() {
- return nil, errEmptyQueue()
- }
- return q.q[(q.last+cap(q.q)-1)%cap(q.q)], nil
-}
-
-// push adds an item to the tail of partialResultQueue.
-func (q *partialResultQueue) push(r *sppb.PartialResultSet) {
- if q.q == nil {
- q.q = make([]*sppb.PartialResultSet, 8 /* arbitrary */)
- }
- if q.n == cap(q.q) {
- buf := make([]*sppb.PartialResultSet, cap(q.q)*2)
- for i := 0; i < q.n; i++ {
- buf[i] = q.q[(q.first+i)%cap(q.q)]
- }
- q.q = buf
- q.first = 0
- q.last = q.n
- }
- q.q[q.last] = r
- q.last = (q.last + 1) % cap(q.q)
- q.n++
-}
-
-// pop removes an item from the head of partialResultQueue and returns
-// it.
-func (q *partialResultQueue) pop() *sppb.PartialResultSet {
- if q.n == 0 {
- return nil
- }
- r := q.q[q.first]
- q.q[q.first] = nil
- q.first = (q.first + 1) % cap(q.q)
- q.n--
- return r
-}
-
-// clear empties partialResultQueue.
-func (q *partialResultQueue) clear() {
- *q = partialResultQueue{}
-}
-
-// dump retrieves all items from partialResultQueue and return them in a slice.
-// It is used only in tests.
-func (q *partialResultQueue) dump() []*sppb.PartialResultSet {
- var dq []*sppb.PartialResultSet
- for i := q.first; len(dq) < q.n; i = (i + 1) % cap(q.q) {
- dq = append(dq, q.q[i])
- }
- return dq
-}
-
-// resumableStreamDecoderState encodes resumableStreamDecoder's status.
-// See also the comments for resumableStreamDecoder.Next.
-type resumableStreamDecoderState int
-
-const (
- unConnected resumableStreamDecoderState = iota // 0
- queueingRetryable // 1
- queueingUnretryable // 2
- aborted // 3
- finished // 4
-)
-
-// resumableStreamDecoder provides a resumable interface for receiving
-// sppb.PartialResultSet(s) from a given query wrapped by
-// resumableStreamDecoder.rpc().
-type resumableStreamDecoder struct {
- // state is the current status of resumableStreamDecoder, see also
- // the comments for resumableStreamDecoder.Next.
- state resumableStreamDecoderState
- // stateWitness when non-nil is called to observe state change,
- // used for testing.
- stateWitness func(resumableStreamDecoderState)
- // ctx is the caller's context, used for cancel/timeout Next().
- ctx context.Context
- // rpc is a factory of streamingReceiver, which might resume
- // a pervious stream from the point encoded in restartToken.
- // rpc is always a wrapper of a Cloud Spanner query which is
- // resumable.
- rpc func(ctx context.Context, restartToken []byte) (streamingReceiver, error)
- // stream is the current RPC streaming receiver.
- stream streamingReceiver
- // q buffers received yet undecoded partial results.
- q partialResultQueue
- // bytesBetweenResumeTokens is the proxy of the byte size of PartialResultSets being queued
- // between two resume tokens. Once bytesBetweenResumeTokens is greater than
- // maxBytesBetweenResumeTokens, resumableStreamDecoder goes into queueingUnretryable state.
- bytesBetweenResumeTokens int32
- // maxBytesBetweenResumeTokens is the max number of bytes that can be buffered
- // between two resume tokens. It is always copied from the global maxBytesBetweenResumeTokens
- // atomically.
- maxBytesBetweenResumeTokens int32
- // np is the next sppb.PartialResultSet ready to be returned
- // to caller of resumableStreamDecoder.Get().
- np *sppb.PartialResultSet
- // resumeToken stores the resume token that resumableStreamDecoder has
- // last revealed to caller.
- resumeToken []byte
- // retryCount is the number of retries that have been carried out so far
- retryCount int
- // err is the last error resumableStreamDecoder has encountered so far.
- err error
- // backoff to compute delays between retries.
- backoff exponentialBackoff
-}
-
-// newResumableStreamDecoder creates a new resumeableStreamDecoder instance.
-// Parameter rpc should be a function that creates a new stream
-// beginning at the restartToken if non-nil.
-func newResumableStreamDecoder(ctx context.Context, rpc func(ct context.Context, restartToken []byte) (streamingReceiver, error)) *resumableStreamDecoder {
- return &resumableStreamDecoder{
- ctx: ctx,
- rpc: rpc,
- maxBytesBetweenResumeTokens: atomic.LoadInt32(&maxBytesBetweenResumeTokens),
- backoff: defaultBackoff,
- }
-}
-
-// changeState fulfills state transition for resumableStateDecoder.
-func (d *resumableStreamDecoder) changeState(target resumableStreamDecoderState) {
- if d.state == queueingRetryable && d.state != target {
- // Reset bytesBetweenResumeTokens because it is only meaningful/changed under
- // queueingRetryable state.
- d.bytesBetweenResumeTokens = 0
- }
- d.state = target
- if d.stateWitness != nil {
- d.stateWitness(target)
- }
-}
-
-// isNewResumeToken returns if the observed resume token is different from
-// the one returned from server last time.
-func (d *resumableStreamDecoder) isNewResumeToken(rt []byte) bool {
- if rt == nil {
- return false
- }
- if bytes.Compare(rt, d.resumeToken) == 0 {
- return false
- }
- return true
-}
-
-// Next advances to the next available partial result set. If error or no
-// more, returns false, call Err to determine if an error was encountered.
-// The following diagram illustrates the state machine of resumableStreamDecoder
-// that Next() implements. Note that state transition can be only triggered by
-// RPC activities.
-/*
- rpc() fails retryable
- +---------+
- | | rpc() fails unretryable/ctx timeouts or cancelled
- | | +------------------------------------------------+
- | | | |
- | v | v
- | +---+---+---+ +--------+ +------+--+
- +-----+unConnected| |finished| | aborted |<----+
- | | ++-----+-+ +------+--+ |
- +---+----+--+ ^ ^ ^ |
- | ^ | | | |
- | | | | recv() fails |
- | | | | | |
- | |recv() fails retryable | | | |
- | |with valid ctx | | | |
- | | | | | |
- rpc() succeeds | +-----------------------+ | | |
- | | | recv EOF recv EOF | |
- | | | | | |
- v | | Queue size exceeds | | |
- +---+----+---+----+threshold +-------+-----------+ | |
-+---------->+ +--------------->+ +-+ |
-| |queueingRetryable| |queueingUnretryable| |
-| | +<---------------+ | |
-| +---+----------+--+ pop() returns +--+----+-----------+ |
-| | | resume token | ^ |
-| | | | | |
-| | | | | |
-+---------------+ | | | |
- recv() succeeds | +----+ |
- | recv() succeeds |
- | |
- | |
- | |
- | |
- | |
- +--------------------------------------------------+
- recv() fails unretryable
-
-*/
-var (
- // maxBytesBetweenResumeTokens is the maximum amount of bytes that resumableStreamDecoder
- // in queueingRetryable state can use to queue PartialResultSets before getting
- // into queueingUnretryable state.
- maxBytesBetweenResumeTokens = int32(128 * 1024 * 1024)
-)
-
-func (d *resumableStreamDecoder) next() bool {
- for {
- select {
- case <-d.ctx.Done():
- // Do context check here so that even gRPC failed to do
- // so, resumableStreamDecoder can still break the loop
- // as expected.
- d.err = errContextCanceled(d.ctx, d.err)
- d.changeState(aborted)
- default:
- }
- switch d.state {
- case unConnected:
- // If no gRPC stream is available, try to initiate one.
- if d.stream, d.err = d.rpc(d.ctx, d.resumeToken); d.err != nil {
- if isRetryable(d.err) {
- d.doBackOff()
- // Be explicit about state transition, although the
- // state doesn't actually change. State transition
- // will be triggered only by RPC activity, regardless of
- // whether there is an actual state change or not.
- d.changeState(unConnected)
- continue
- }
- d.changeState(aborted)
- continue
- }
- d.resetBackOff()
- d.changeState(queueingRetryable)
- continue
- case queueingRetryable:
- fallthrough
- case queueingUnretryable:
- // Receiving queue is not empty.
- last, err := d.q.peekLast()
- if err != nil {
- // Only the case that receiving queue is empty could cause peekLast to
- // return error and in such case, we should try to receive from stream.
- d.tryRecv()
- continue
- }
- if d.isNewResumeToken(last.ResumeToken) {
- // Got new resume token, return buffered sppb.PartialResultSets to caller.
- d.np = d.q.pop()
- if d.q.empty() {
- d.bytesBetweenResumeTokens = 0
- // The new resume token was just popped out from queue, record it.
- d.resumeToken = d.np.ResumeToken
- d.changeState(queueingRetryable)
- }
- return true
- }
- if d.bytesBetweenResumeTokens >= d.maxBytesBetweenResumeTokens && d.state == queueingRetryable {
- d.changeState(queueingUnretryable)
- continue
- }
- if d.state == queueingUnretryable {
- // When there is no resume token observed,
- // only yield sppb.PartialResultSets to caller under
- // queueingUnretryable state.
- d.np = d.q.pop()
- return true
- }
- // Needs to receive more from gRPC stream till a new resume token
- // is observed.
- d.tryRecv()
- continue
- case aborted:
- // Discard all pending items because none of them
- // should be yield to caller.
- d.q.clear()
- return false
- case finished:
- // If query has finished, check if there are still buffered messages.
- if d.q.empty() {
- // No buffered PartialResultSet.
- return false
- }
- // Although query has finished, there are still buffered PartialResultSets.
- d.np = d.q.pop()
- return true
-
- default:
- log.Errorf("Unexpected resumableStreamDecoder.state: %v", d.state)
- return false
- }
- }
-}
-
-// tryRecv attempts to receive a PartialResultSet from gRPC stream.
-func (d *resumableStreamDecoder) tryRecv() {
- var res *sppb.PartialResultSet
- if res, d.err = d.stream.Recv(); d.err != nil {
- if d.err == io.EOF {
- d.err = nil
- d.changeState(finished)
- return
- }
- if isRetryable(d.err) && d.state == queueingRetryable {
- d.err = nil
- // Discard all queue items (none have resume tokens).
- d.q.clear()
- d.stream = nil
- d.changeState(unConnected)
- d.doBackOff()
- return
- }
- d.changeState(aborted)
- return
- }
- d.q.push(res)
- if d.state == queueingRetryable && !d.isNewResumeToken(res.ResumeToken) {
- // adjusting d.bytesBetweenResumeTokens
- d.bytesBetweenResumeTokens += int32(proto.Size(res))
- }
- d.resetBackOff()
- d.changeState(d.state)
-}
-
-// resetBackOff clears the internal retry counter of
-// resumableStreamDecoder so that the next exponential
-// backoff will start at a fresh state.
-func (d *resumableStreamDecoder) resetBackOff() {
- d.retryCount = 0
-}
-
-// doBackoff does an exponential backoff sleep.
-func (d *resumableStreamDecoder) doBackOff() {
- ticker := time.NewTicker(d.backoff.delay(d.retryCount))
- defer ticker.Stop()
- d.retryCount++
- select {
- case <-d.ctx.Done():
- case <-ticker.C:
- }
-}
-
-// get returns the most recent PartialResultSet generated by a call to next.
-func (d *resumableStreamDecoder) get() *sppb.PartialResultSet {
- return d.np
-}
-
-// lastErr returns the last non-EOF error encountered.
-func (d *resumableStreamDecoder) lastErr() error {
- return d.err
-}
-
-// partialResultSetDecoder assembles PartialResultSet(s) into Cloud Spanner
-// Rows.
-type partialResultSetDecoder struct {
- row Row
- tx *sppb.Transaction
- chunked bool // if true, next value should be merged with last values entry.
- ts time.Time // read timestamp
-}
-
-// yield checks we have a complete row, and if so returns it. A row is not
-// complete if it doesn't have enough columns, or if this is a chunked response
-// and there are no further values to process.
-func (p *partialResultSetDecoder) yield(chunked, last bool) *Row {
- if len(p.row.vals) == len(p.row.fields) && (!chunked || !last) {
- // When partialResultSetDecoder gets enough number of
- // Column values, There are two cases that a new Row
- // should be yield:
- // 1. The incoming PartialResultSet is not chunked;
- // 2. The incoming PartialResultSet is chunked, but the
- // proto3.Value being merged is not the last one in
- // the PartialResultSet.
- //
- // Use a fresh Row to simplify clients that want to use yielded results
- // after the next row is retrieved. Note that fields is never changed
- // so it doesn't need to be copied.
- fresh := Row{
- fields: p.row.fields,
- vals: make([]*proto3.Value, len(p.row.vals)),
- }
- copy(fresh.vals, p.row.vals)
- p.row.vals = p.row.vals[:0] // empty and reuse slice
- return &fresh
- }
- return nil
-}
-
-// yieldTx returns transaction information via caller supplied callback.
-func errChunkedEmptyRow() error {
- return spannerErrorf(codes.FailedPrecondition, "got invalid chunked PartialResultSet with empty Row")
-}
-
-// add tries to merge a new PartialResultSet into buffered Row. It returns
-// any rows that have been completed as a result.
-func (p *partialResultSetDecoder) add(r *sppb.PartialResultSet) ([]*Row, error) {
- var rows []*Row
- if r.Metadata != nil {
- // Metadata should only be returned in the first result.
- if p.row.fields == nil {
- p.row.fields = r.Metadata.RowType.Fields
- }
- if p.tx == nil && r.Metadata.Transaction != nil {
- p.tx = r.Metadata.Transaction
- if p.tx.ReadTimestamp != nil {
- p.ts = time.Unix(p.tx.ReadTimestamp.Seconds, int64(p.tx.ReadTimestamp.Nanos))
- }
- }
- }
- if len(r.Values) == 0 {
- return nil, nil
- }
- if p.chunked {
- p.chunked = false
- // Try to merge first value in r.Values into
- // uncompleted row.
- last := len(p.row.vals) - 1
- if last < 0 { // sanity check
- return nil, errChunkedEmptyRow()
- }
- var err error
- // If p is chunked, then we should always try to merge p.last with r.first.
- if p.row.vals[last], err = p.merge(p.row.vals[last], r.Values[0]); err != nil {
- return nil, err
- }
- r.Values = r.Values[1:]
- // Merge is done, try to yield a complete Row.
- if row := p.yield(r.ChunkedValue, len(r.Values) == 0); row != nil {
- rows = append(rows, row)
- }
- }
- for i, v := range r.Values {
- // The rest values in r can be appened into p directly.
- p.row.vals = append(p.row.vals, v)
- // Again, check to see if a complete Row can be yielded because of
- // the newly added value.
- if row := p.yield(r.ChunkedValue, i == len(r.Values)-1); row != nil {
- rows = append(rows, row)
- }
- }
- if r.ChunkedValue {
- // After dealing with all values in r, if r is chunked then p must
- // be also chunked.
- p.chunked = true
- }
- return rows, nil
-}
-
-// isMergeable returns if a protobuf Value can be potentially merged with
-// other protobuf Values.
-func (p *partialResultSetDecoder) isMergeable(a *proto3.Value) bool {
- switch a.Kind.(type) {
- case *proto3.Value_StringValue:
- return true
- case *proto3.Value_ListValue:
- return true
- default:
- return false
- }
-}
-
-// errIncompatibleMergeTypes returns error for incompatible protobuf types
-// that cannot be merged by partialResultSetDecoder.
-func errIncompatibleMergeTypes(a, b *proto3.Value) error {
- return spannerErrorf(codes.FailedPrecondition, "incompatible type in chunked PartialResultSet. expected (%T), got (%T)", a.Kind, b.Kind)
-}
-
-// errUnsupportedMergeType returns error for protobuf type that cannot be
-// merged to other protobufs.
-func errUnsupportedMergeType(a *proto3.Value) error {
- return spannerErrorf(codes.FailedPrecondition, "unsupported type merge (%T)", a.Kind)
-}
-
-// merge tries to combine two protobuf Values if possible.
-func (p *partialResultSetDecoder) merge(a, b *proto3.Value) (*proto3.Value, error) {
- var err error
- typeErr := errIncompatibleMergeTypes(a, b)
- switch t := a.Kind.(type) {
- case *proto3.Value_StringValue:
- s, ok := b.Kind.(*proto3.Value_StringValue)
- if !ok {
- return nil, typeErr
- }
- return &proto3.Value{
- Kind: &proto3.Value_StringValue{StringValue: t.StringValue + s.StringValue},
- }, nil
- case *proto3.Value_ListValue:
- l, ok := b.Kind.(*proto3.Value_ListValue)
- if !ok {
- return nil, typeErr
- }
- if l.ListValue == nil || len(l.ListValue.Values) <= 0 {
- // b is an empty list, just return a.
- return a, nil
- }
- if t.ListValue == nil || len(t.ListValue.Values) <= 0 {
- // a is an empty list, just return b.
- return b, nil
- }
- if la := len(t.ListValue.Values) - 1; p.isMergeable(t.ListValue.Values[la]) {
- // When the last item in a is of type String,
- // List or Struct(encoded into List by Cloud Spanner),
- // try to Merge last item in a and first item in b.
- t.ListValue.Values[la], err = p.merge(t.ListValue.Values[la], l.ListValue.Values[0])
- if err != nil {
- return nil, err
- }
- l.ListValue.Values = l.ListValue.Values[1:]
- }
- return &proto3.Value{
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: append(t.ListValue.Values, l.ListValue.Values...),
- },
- },
- }, nil
- default:
- return nil, errUnsupportedMergeType(a)
- }
-
-}
-
-// Done returns if partialResultSetDecoder has already done with all buffered
-// values.
-func (p *partialResultSetDecoder) done() bool {
- // There is no explicit end of stream marker, but ending part way
- // through a row is obviously bad, or ending with the last column still
- // awaiting completion.
- return len(p.row.vals) == 0 && !p.chunked
-}
diff --git a/vendor/cloud.google.com/go/spanner/read_test.go b/vendor/cloud.google.com/go/spanner/read_test.go
deleted file mode 100644
index db50110ab..000000000
--- a/vendor/cloud.google.com/go/spanner/read_test.go
+++ /dev/null
@@ -1,1733 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "errors"
- "fmt"
- "io"
- "reflect"
- "sync/atomic"
- "testing"
- "time"
-
- "golang.org/x/net/context"
-
- proto "github.com/golang/protobuf/proto"
- proto3 "github.com/golang/protobuf/ptypes/struct"
-
- "cloud.google.com/go/spanner/internal/testutil"
- "google.golang.org/api/iterator"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
-)
-
-var (
- // Mocked transaction timestamp.
- trxTs = time.Unix(1, 2)
- // Metadata for mocked KV table, its rows are returned by SingleUse transactions.
- kvMeta = func() *sppb.ResultSetMetadata {
- meta := testutil.KvMeta
- meta.Transaction = &sppb.Transaction{
- ReadTimestamp: timestampProto(trxTs),
- }
- return &meta
- }()
- // Metadata for mocked ListKV table, which uses List for its key and value.
- // Its rows are returned by snapshot readonly transactions, as indicated in the transaction metadata.
- kvListMeta = &sppb.ResultSetMetadata{
- RowType: &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- {
- Name: "Key",
- Type: &sppb.Type{
- Code: sppb.TypeCode_ARRAY,
- ArrayElementType: &sppb.Type{
- Code: sppb.TypeCode_STRING,
- },
- },
- },
- {
- Name: "Value",
- Type: &sppb.Type{
- Code: sppb.TypeCode_ARRAY,
- ArrayElementType: &sppb.Type{
- Code: sppb.TypeCode_STRING,
- },
- },
- },
- },
- },
- Transaction: &sppb.Transaction{
- Id: transactionID{5, 6, 7, 8, 9},
- ReadTimestamp: timestampProto(trxTs),
- },
- }
- // Metadata for mocked schema of a query result set, which has two struct
- // columns named "Col1" and "Col2", the struct's schema is like the
- // following:
- //
- // STRUCT {
- // INT
- // LIST<STRING>
- // }
- //
- // Its rows are returned in readwrite transaction, as indicated in the transaction metadata.
- kvObjectMeta = &sppb.ResultSetMetadata{
- RowType: &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- {
- Name: "Col1",
- Type: &sppb.Type{
- Code: sppb.TypeCode_STRUCT,
- StructType: &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- {
- Name: "foo-f1",
- Type: &sppb.Type{
- Code: sppb.TypeCode_INT64,
- },
- },
- {
- Name: "foo-f2",
- Type: &sppb.Type{
- Code: sppb.TypeCode_ARRAY,
- ArrayElementType: &sppb.Type{
- Code: sppb.TypeCode_STRING,
- },
- },
- },
- },
- },
- },
- },
- {
- Name: "Col2",
- Type: &sppb.Type{
- Code: sppb.TypeCode_STRUCT,
- StructType: &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- {
- Name: "bar-f1",
- Type: &sppb.Type{
- Code: sppb.TypeCode_INT64,
- },
- },
- {
- Name: "bar-f2",
- Type: &sppb.Type{
- Code: sppb.TypeCode_ARRAY,
- ArrayElementType: &sppb.Type{
- Code: sppb.TypeCode_STRING,
- },
- },
- },
- },
- },
- },
- },
- },
- },
- Transaction: &sppb.Transaction{
- Id: transactionID{1, 2, 3, 4, 5},
- },
- }
-)
-
-// String implements fmt.stringer.
-func (r *Row) String() string {
- return fmt.Sprintf("{fields: %s, val: %s}", r.fields, r.vals)
-}
-
-func describeRows(l []*Row) string {
- // generate a nice test failure description
- var s = "["
- for i, r := range l {
- if i != 0 {
- s += ",\n "
- }
- s += fmt.Sprint(r)
- }
- s += "]"
- return s
-}
-
-// Helper for generating proto3 Value_ListValue instances, making
-// test code shorter and readable.
-func genProtoListValue(v ...string) *proto3.Value_ListValue {
- r := &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{},
- },
- }
- for _, e := range v {
- r.ListValue.Values = append(
- r.ListValue.Values,
- &proto3.Value{
- Kind: &proto3.Value_StringValue{StringValue: e},
- },
- )
- }
- return r
-}
-
-// Test Row generation logics of partialResultSetDecoder.
-func TestPartialResultSetDecoder(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- var tests = []struct {
- input []*sppb.PartialResultSet
- wantF []*Row
- wantTxID transactionID
- wantTs time.Time
- wantD bool
- }{
- {
- // Empty input.
- wantD: true,
- },
- // String merging examples.
- {
- // Single KV result.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- {Kind: &proto3.Value_StringValue{StringValue: "bar"}},
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- {Kind: &proto3.Value_StringValue{StringValue: "bar"}},
- },
- },
- },
- wantTs: trxTs,
- wantD: true,
- },
- {
- // Incomplete partial result.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- },
- },
- },
- wantTs: trxTs,
- wantD: false,
- },
- {
- // Complete splitted result.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- },
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "bar"}},
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- {Kind: &proto3.Value_StringValue{StringValue: "bar"}},
- },
- },
- },
- wantTs: trxTs,
- wantD: true,
- },
- {
- // Multi-row example with splitted row in the middle.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- {Kind: &proto3.Value_StringValue{StringValue: "bar"}},
- {Kind: &proto3.Value_StringValue{StringValue: "A"}},
- },
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "1"}},
- {Kind: &proto3.Value_StringValue{StringValue: "B"}},
- {Kind: &proto3.Value_StringValue{StringValue: "2"}},
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "foo"}},
- {Kind: &proto3.Value_StringValue{StringValue: "bar"}},
- },
- },
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "A"}},
- {Kind: &proto3.Value_StringValue{StringValue: "1"}},
- },
- },
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "B"}},
- {Kind: &proto3.Value_StringValue{StringValue: "2"}},
- },
- },
- },
- wantTs: trxTs,
- wantD: true,
- },
- {
- // Merging example in result_set.proto.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "Hello"}},
- {Kind: &proto3.Value_StringValue{StringValue: "W"}},
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "orl"}},
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "d"}},
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "Hello"}},
- {Kind: &proto3.Value_StringValue{StringValue: "World"}},
- },
- },
- },
- wantTs: trxTs,
- wantD: true,
- },
- {
- // More complex example showing completing a merge and
- // starting a new merge in the same partialResultSet.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "Hello"}},
- {Kind: &proto3.Value_StringValue{StringValue: "W"}}, // start split in value
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "orld"}}, // complete value
- {Kind: &proto3.Value_StringValue{StringValue: "i"}}, // start split in key
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "s"}}, // complete key
- {Kind: &proto3.Value_StringValue{StringValue: "not"}},
- {Kind: &proto3.Value_StringValue{StringValue: "a"}},
- {Kind: &proto3.Value_StringValue{StringValue: "qu"}}, // split in value
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "estion"}}, // complete value
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "Hello"}},
- {Kind: &proto3.Value_StringValue{StringValue: "World"}},
- },
- },
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "is"}},
- {Kind: &proto3.Value_StringValue{StringValue: "not"}},
- },
- },
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: "a"}},
- {Kind: &proto3.Value_StringValue{StringValue: "question"}},
- },
- },
- },
- wantTs: trxTs,
- wantD: true,
- },
- // List merging examples.
- {
- // Non-splitting Lists.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvListMeta,
- Values: []*proto3.Value{
- {
- Kind: genProtoListValue("foo-1", "foo-2"),
- },
- },
- },
- {
- Values: []*proto3.Value{
- {
- Kind: genProtoListValue("bar-1", "bar-2"),
- },
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvListMeta.RowType.Fields,
- vals: []*proto3.Value{
- {
- Kind: genProtoListValue("foo-1", "foo-2"),
- },
- {
- Kind: genProtoListValue("bar-1", "bar-2"),
- },
- },
- },
- },
- wantTxID: transactionID{5, 6, 7, 8, 9},
- wantTs: trxTs,
- wantD: true,
- },
- {
- // Simple List merge case: splitted string element.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvListMeta,
- Values: []*proto3.Value{
- {
- Kind: genProtoListValue("foo-1", "foo-"),
- },
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {
- Kind: genProtoListValue("2"),
- },
- },
- },
- {
- Values: []*proto3.Value{
- {
- Kind: genProtoListValue("bar-1", "bar-2"),
- },
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvListMeta.RowType.Fields,
- vals: []*proto3.Value{
- {
- Kind: genProtoListValue("foo-1", "foo-2"),
- },
- {
- Kind: genProtoListValue("bar-1", "bar-2"),
- },
- },
- },
- },
- wantTxID: transactionID{5, 6, 7, 8, 9},
- wantTs: trxTs,
- wantD: true,
- },
- {
- // Struct merging is also implemented by List merging. Note that
- // Cloud Spanner uses proto.ListValue to encode Structs as well.
- input: []*sppb.PartialResultSet{
- {
- Metadata: kvObjectMeta,
- Values: []*proto3.Value{
- {
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{
- {Kind: &proto3.Value_NumberValue{NumberValue: 23}},
- {Kind: genProtoListValue("foo-1", "fo")},
- },
- },
- },
- },
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{
- {Kind: genProtoListValue("o-2", "f")},
- },
- },
- },
- },
- },
- ChunkedValue: true,
- },
- {
- Values: []*proto3.Value{
- {
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{
- {Kind: genProtoListValue("oo-3")},
- },
- },
- },
- },
- {
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{
- {Kind: &proto3.Value_NumberValue{NumberValue: 45}},
- {Kind: genProtoListValue("bar-1")},
- },
- },
- },
- },
- },
- },
- },
- wantF: []*Row{
- {
- fields: kvObjectMeta.RowType.Fields,
- vals: []*proto3.Value{
- {
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{
- {Kind: &proto3.Value_NumberValue{NumberValue: 23}},
- {Kind: genProtoListValue("foo-1", "foo-2", "foo-3")},
- },
- },
- },
- },
- {
- Kind: &proto3.Value_ListValue{
- ListValue: &proto3.ListValue{
- Values: []*proto3.Value{
- {Kind: &proto3.Value_NumberValue{NumberValue: 45}},
- {Kind: genProtoListValue("bar-1")},
- },
- },
- },
- },
- },
- },
- },
- wantTxID: transactionID{1, 2, 3, 4, 5},
- wantD: true,
- },
- }
-
-nextTest:
- for i, test := range tests {
- var rows []*Row
- p := &partialResultSetDecoder{}
- for j, v := range test.input {
- rs, err := p.add(v)
- if err != nil {
- t.Errorf("test %d.%d: partialResultSetDecoder.add(%v) = %v; want nil", i, j, v, err)
- continue nextTest
- }
- rows = append(rows, rs...)
- }
- if !reflect.DeepEqual(p.ts, test.wantTs) {
- t.Errorf("got transaction(%v), want %v", p.ts, test.wantTs)
- }
- if !reflect.DeepEqual(rows, test.wantF) {
- t.Errorf("test %d: rows=\n%v\n; want\n%v\n; p.row:\n%v\n", i, describeRows(rows), describeRows(test.wantF), p.row)
- }
- if got := p.done(); got != test.wantD {
- t.Errorf("test %d: partialResultSetDecoder.done() = %v", i, got)
- }
- }
-}
-
-const (
- maxBuffers = 16 // max number of PartialResultSets that will be buffered in tests.
-)
-
-// setMaxBytesBetweenResumeTokens sets the global maxBytesBetweenResumeTokens to a smaller
-// value more suitable for tests. It returns a function which should be called to restore
-// the maxBytesBetweenResumeTokens to its old value
-func setMaxBytesBetweenResumeTokens() func() {
- o := atomic.LoadInt32(&maxBytesBetweenResumeTokens)
- atomic.StoreInt32(&maxBytesBetweenResumeTokens, int32(maxBuffers*proto.Size(&sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}},
- },
- })))
- return func() {
- atomic.StoreInt32(&maxBytesBetweenResumeTokens, o)
- }
-}
-
-// keyStr generates key string for kvMeta schema.
-func keyStr(i int) string {
- return fmt.Sprintf("foo-%02d", i)
-}
-
-// valStr generates value string for kvMeta schema.
-func valStr(i int) string {
- return fmt.Sprintf("bar-%02d", i)
-}
-
-// Test state transitions of resumableStreamDecoder where state machine
-// ends up to a non-blocking state(resumableStreamDecoder.Next returns
-// on non-blocking state).
-func TestRsdNonblockingStates(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- tests := []struct {
- name string
- msgs []testutil.MockCtlMsg
- rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error)
- sql string
- // Expected values
- want []*sppb.PartialResultSet // PartialResultSets that should be returned to caller
- queue []*sppb.PartialResultSet // PartialResultSets that should be buffered
- resumeToken []byte // Resume token that is maintained by resumableStreamDecoder
- stateHistory []resumableStreamDecoderState // State transition history of resumableStreamDecoder
- wantErr error
- }{
- {
- // unConnected->queueingRetryable->finished
- name: "unConnected->queueingRetryable->finished",
- msgs: []testutil.MockCtlMsg{
- {},
- {},
- {Err: io.EOF, ResumeToken: false},
- },
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}},
- },
- },
- },
- queue: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}},
- },
- },
- },
- stateHistory: []resumableStreamDecoderState{
- queueingRetryable, // do RPC
- queueingRetryable, // got foo-00
- queueingRetryable, // got foo-01
- finished, // got EOF
- },
- },
- {
- // unConnected->queueingRetryable->aborted
- name: "unConnected->queueingRetryable->aborted",
- msgs: []testutil.MockCtlMsg{
- {},
- {Err: nil, ResumeToken: true},
- {},
- {Err: errors.New("I quit"), ResumeToken: false},
- },
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}},
- },
- },
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}},
- },
- ResumeToken: testutil.EncodeResumeToken(1),
- },
- },
- stateHistory: []resumableStreamDecoderState{
- queueingRetryable, // do RPC
- queueingRetryable, // got foo-00
- queueingRetryable, // got foo-01
- queueingRetryable, // foo-01, resume token
- queueingRetryable, // got foo-02
- aborted, // got error
- },
- wantErr: grpc.Errorf(codes.Unknown, "I quit"),
- },
- {
- // unConnected->queueingRetryable->queueingUnretryable->queueingUnretryable
- name: "unConnected->queueingRetryable->queueingUnretryable->queueingUnretryable",
- msgs: func() (m []testutil.MockCtlMsg) {
- for i := 0; i < maxBuffers+1; i++ {
- m = append(m, testutil.MockCtlMsg{})
- }
- return m
- }(),
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: func() (s []*sppb.PartialResultSet) {
- for i := 0; i < maxBuffers+1; i++ {
- s = append(s, &sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- }
- return s
- }(),
- stateHistory: func() (s []resumableStreamDecoderState) {
- s = append(s, queueingRetryable) // RPC
- for i := 0; i < maxBuffers; i++ {
- s = append(s, queueingRetryable) // the internal queue of resumableStreamDecoder fills up
- }
- // the first item fills up the queue and triggers state transition;
- // the second item is received under queueingUnretryable state.
- s = append(s, queueingUnretryable)
- s = append(s, queueingUnretryable)
- return s
- }(),
- },
- {
- // unConnected->queueingRetryable->queueingUnretryable->aborted
- name: "unConnected->queueingRetryable->queueingUnretryable->aborted",
- msgs: func() (m []testutil.MockCtlMsg) {
- for i := 0; i < maxBuffers; i++ {
- m = append(m, testutil.MockCtlMsg{})
- }
- m = append(m, testutil.MockCtlMsg{Err: errors.New("Just Abort It"), ResumeToken: false})
- return m
- }(),
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: func() (s []*sppb.PartialResultSet) {
- for i := 0; i < maxBuffers; i++ {
- s = append(s, &sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- }
- return s
- }(),
- stateHistory: func() (s []resumableStreamDecoderState) {
- s = append(s, queueingRetryable) // RPC
- for i := 0; i < maxBuffers; i++ {
- s = append(s, queueingRetryable) // internal queue of resumableStreamDecoder fills up
- }
- s = append(s, queueingUnretryable) // the last row triggers state change
- s = append(s, aborted) // Error happens
- return s
- }(),
- wantErr: grpc.Errorf(codes.Unknown, "Just Abort It"),
- },
- }
-nextTest:
- for _, test := range tests {
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- opts := []grpc.DialOption{
- grpc.WithInsecure(),
- }
- cc, err := grpc.Dial(ms.Addr(), opts...)
- if err != nil {
- t.Fatalf("%v: Dial(%q) = %v", test.name, ms.Addr(), err)
- }
- mc := sppb.NewSpannerClient(cc)
- if test.rpc == nil {
- test.rpc = func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: test.sql,
- ResumeToken: resumeToken,
- })
- }
- }
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- r := newResumableStreamDecoder(
- ctx,
- test.rpc,
- )
- st := []resumableStreamDecoderState{}
- var lastErr error
- // Once the expected number of state transitions are observed,
- // send a signal by setting stateDone = true.
- stateDone := false
- // Set stateWitness to listen to state changes.
- hl := len(test.stateHistory) // To avoid data race on test.
- r.stateWitness = func(rs resumableStreamDecoderState) {
- if !stateDone {
- // Record state transitions.
- st = append(st, rs)
- if len(st) == hl {
- lastErr = r.lastErr()
- stateDone = true
- }
- }
- }
- // Let mock server stream given messages to resumableStreamDecoder.
- for _, m := range test.msgs {
- ms.AddMsg(m.Err, m.ResumeToken)
- }
- var rs []*sppb.PartialResultSet
- for {
- select {
- case <-ctx.Done():
- t.Errorf("context cancelled or timeout during test")
- continue nextTest
- default:
- }
- if stateDone {
- // Check if resumableStreamDecoder carried out expected
- // state transitions.
- if !reflect.DeepEqual(st, test.stateHistory) {
- t.Errorf("%v: observed state transitions: \n%v\n, want \n%v\n",
- test.name, st, test.stateHistory)
- }
- // Check if resumableStreamDecoder returns expected array of
- // PartialResultSets.
- if !reflect.DeepEqual(rs, test.want) {
- t.Errorf("%v: received PartialResultSets: \n%v\n, want \n%v\n", test.name, rs, test.want)
- }
- // Verify that resumableStreamDecoder's internal buffering is also correct.
- var q []*sppb.PartialResultSet
- for {
- item := r.q.pop()
- if item == nil {
- break
- }
- q = append(q, item)
- }
- if !reflect.DeepEqual(q, test.queue) {
- t.Errorf("%v: PartialResultSets still queued: \n%v\n, want \n%v\n", test.name, q, test.queue)
- }
- // Verify resume token.
- if test.resumeToken != nil && !reflect.DeepEqual(r.resumeToken, test.resumeToken) {
- t.Errorf("%v: Resume token is %v, want %v\n", test.name, r.resumeToken, test.resumeToken)
- }
- // Verify error message.
- if !reflect.DeepEqual(lastErr, test.wantErr) {
- t.Errorf("%v: got error %v, want %v", test.name, lastErr, test.wantErr)
- }
- // Proceed to next test
- continue nextTest
- }
- // Receive next decoded item.
- if r.next() {
- rs = append(rs, r.get())
- }
- }
- }
-}
-
-// Test state transitions of resumableStreamDecoder where state machine
-// ends up to a blocking state(resumableStreamDecoder.Next blocks
-// on blocking state).
-func TestRsdBlockingStates(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- tests := []struct {
- name string
- msgs []testutil.MockCtlMsg
- rpc func(ct context.Context, resumeToken []byte) (streamingReceiver, error)
- sql string
- // Expected values
- want []*sppb.PartialResultSet // PartialResultSets that should be returned to caller
- queue []*sppb.PartialResultSet // PartialResultSets that should be buffered
- resumeToken []byte // Resume token that is maintained by resumableStreamDecoder
- stateHistory []resumableStreamDecoderState // State transition history of resumableStreamDecoder
- wantErr error
- }{
- {
- // unConnected -> unConnected
- name: "unConnected -> unConnected",
- rpc: func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return nil, grpc.Errorf(codes.Unavailable, "trust me: server is unavailable")
- },
- sql: "SELECT * from t_whatever",
- stateHistory: []resumableStreamDecoderState{unConnected, unConnected, unConnected},
- wantErr: grpc.Errorf(codes.Unavailable, "trust me: server is unavailable"),
- },
- {
- // unConnected -> queueingRetryable
- name: "unConnected -> queueingRetryable",
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- stateHistory: []resumableStreamDecoderState{queueingRetryable},
- },
- {
- // unConnected->queueingRetryable->queueingRetryable
- name: "unConnected->queueingRetryable->queueingRetryable",
- msgs: []testutil.MockCtlMsg{
- {},
- {Err: nil, ResumeToken: true},
- {Err: nil, ResumeToken: true},
- {},
- },
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}},
- },
- },
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}},
- },
- ResumeToken: testutil.EncodeResumeToken(1),
- },
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(2)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(2)}},
- },
- ResumeToken: testutil.EncodeResumeToken(2),
- },
- },
- queue: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(3)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(3)}},
- },
- },
- },
- resumeToken: testutil.EncodeResumeToken(2),
- stateHistory: []resumableStreamDecoderState{
- queueingRetryable, // do RPC
- queueingRetryable, // got foo-00
- queueingRetryable, // got foo-01
- queueingRetryable, // foo-01, resume token
- queueingRetryable, // got foo-02
- queueingRetryable, // foo-02, resume token
- queueingRetryable, // got foo-03
- },
- },
- {
- // unConnected->queueingRetryable->queueingUnretryable->queueingRetryable->queueingRetryable
- name: "unConnected->queueingRetryable->queueingUnretryable->queueingRetryable->queueingRetryable",
- msgs: func() (m []testutil.MockCtlMsg) {
- for i := 0; i < maxBuffers+1; i++ {
- m = append(m, testutil.MockCtlMsg{})
- }
- m = append(m, testutil.MockCtlMsg{Err: nil, ResumeToken: true})
- m = append(m, testutil.MockCtlMsg{})
- return m
- }(),
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: func() (s []*sppb.PartialResultSet) {
- for i := 0; i < maxBuffers+2; i++ {
- s = append(s, &sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- }
- s[maxBuffers+1].ResumeToken = testutil.EncodeResumeToken(maxBuffers + 1)
- return s
- }(),
- resumeToken: testutil.EncodeResumeToken(maxBuffers + 1),
- queue: []*sppb.PartialResultSet{
- {
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(maxBuffers + 2)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(maxBuffers + 2)}},
- },
- },
- },
- stateHistory: func() (s []resumableStreamDecoderState) {
- s = append(s, queueingRetryable) // RPC
- for i := 0; i < maxBuffers; i++ {
- s = append(s, queueingRetryable) // internal queue of resumableStreamDecoder filles up
- }
- for i := maxBuffers - 1; i < maxBuffers+1; i++ {
- // the first item fills up the queue and triggers state change;
- // the second item is received under queueingUnretryable state.
- s = append(s, queueingUnretryable)
- }
- s = append(s, queueingUnretryable) // got (maxBuffers+1)th row under Unretryable state
- s = append(s, queueingRetryable) // (maxBuffers+1)th row has resume token
- s = append(s, queueingRetryable) // (maxBuffers+2)th row has no resume token
- return s
- }(),
- },
- {
- // unConnected->queueingRetryable->queueingUnretryable->finished
- name: "unConnected->queueingRetryable->queueingUnretryable->finished",
- msgs: func() (m []testutil.MockCtlMsg) {
- for i := 0; i < maxBuffers; i++ {
- m = append(m, testutil.MockCtlMsg{})
- }
- m = append(m, testutil.MockCtlMsg{Err: io.EOF, ResumeToken: false})
- return m
- }(),
- sql: "SELECT t.key key, t.value value FROM t_mock t",
- want: func() (s []*sppb.PartialResultSet) {
- for i := 0; i < maxBuffers; i++ {
- s = append(s, &sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- }
- return s
- }(),
- stateHistory: func() (s []resumableStreamDecoderState) {
- s = append(s, queueingRetryable) // RPC
- for i := 0; i < maxBuffers; i++ {
- s = append(s, queueingRetryable) // internal queue of resumableStreamDecoder fills up
- }
- s = append(s, queueingUnretryable) // last row triggers state change
- s = append(s, finished) // query finishes
- return s
- }(),
- },
- }
- for _, test := range tests {
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- opts := []grpc.DialOption{
- grpc.WithInsecure(),
- }
- cc, err := grpc.Dial(ms.Addr(), opts...)
- if err != nil {
- t.Fatalf("%v: Dial(%q) = %v", test.name, ms.Addr(), err)
- }
- mc := sppb.NewSpannerClient(cc)
- if test.rpc == nil {
- // Avoid using test.sql directly in closure because for loop changes test.
- sql := test.sql
- test.rpc = func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: sql,
- ResumeToken: resumeToken,
- })
- }
- }
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- r := newResumableStreamDecoder(
- ctx,
- test.rpc,
- )
- // Override backoff to make the test run faster.
- r.backoff = exponentialBackoff{1 * time.Nanosecond, 1 * time.Nanosecond}
- // st is the set of observed state transitions.
- st := []resumableStreamDecoderState{}
- // q is the content of the decoder's partial result queue when expected number of state transitions are done.
- q := []*sppb.PartialResultSet{}
- var lastErr error
- // Once the expected number of state transitions are observed,
- // send a signal to channel stateDone.
- stateDone := make(chan int)
- // Set stateWitness to listen to state changes.
- hl := len(test.stateHistory) // To avoid data race on test.
- r.stateWitness = func(rs resumableStreamDecoderState) {
- select {
- case <-stateDone:
- // Noop after expected number of state transitions
- default:
- // Record state transitions.
- st = append(st, rs)
- if len(st) == hl {
- lastErr = r.lastErr()
- q = r.q.dump()
- close(stateDone)
- }
- }
- }
- // Let mock server stream given messages to resumableStreamDecoder.
- for _, m := range test.msgs {
- ms.AddMsg(m.Err, m.ResumeToken)
- }
- var rs []*sppb.PartialResultSet
- go func() {
- for {
- if !r.next() {
- // Note that r.Next also exits on context cancel/timeout.
- return
- }
- rs = append(rs, r.get())
- }
- }()
- // Verify that resumableStreamDecoder reaches expected state.
- select {
- case <-stateDone: // Note that at this point, receiver is still blocking on r.next().
- // Check if resumableStreamDecoder carried out expected
- // state transitions.
- if !reflect.DeepEqual(st, test.stateHistory) {
- t.Errorf("%v: observed state transitions: \n%v\n, want \n%v\n",
- test.name, st, test.stateHistory)
- }
- // Check if resumableStreamDecoder returns expected array of
- // PartialResultSets.
- if !reflect.DeepEqual(rs, test.want) {
- t.Errorf("%v: received PartialResultSets: \n%v\n, want \n%v\n", test.name, rs, test.want)
- }
- // Verify that resumableStreamDecoder's internal buffering is also correct.
- if !reflect.DeepEqual(q, test.queue) {
- t.Errorf("%v: PartialResultSets still queued: \n%v\n, want \n%v\n", test.name, q, test.queue)
- }
- // Verify resume token.
- if test.resumeToken != nil && !reflect.DeepEqual(r.resumeToken, test.resumeToken) {
- t.Errorf("%v: Resume token is %v, want %v\n", test.name, r.resumeToken, test.resumeToken)
- }
- // Verify error message.
- if !reflect.DeepEqual(lastErr, test.wantErr) {
- t.Errorf("%v: got error %v, want %v", test.name, lastErr, test.wantErr)
- }
- case <-time.After(1 * time.Second):
- t.Errorf("%v: Timeout in waiting for state change", test.name)
- }
- ms.Stop()
- cc.Close()
- }
-}
-
-// sReceiver signals every receiving attempt through a channel,
-// used by TestResumeToken to determine if the receiving of a certain
-// PartialResultSet will be attempted next.
-type sReceiver struct {
- c chan int
- rpcReceiver sppb.Spanner_ExecuteStreamingSqlClient
-}
-
-// Recv() implements streamingReceiver.Recv for sReceiver.
-func (sr *sReceiver) Recv() (*sppb.PartialResultSet, error) {
- sr.c <- 1
- return sr.rpcReceiver.Recv()
-}
-
-// waitn waits for nth receiving attempt from now on, until
-// the signal for nth Recv() attempts is received or timeout.
-// Note that because the way stream() works, the signal for the
-// nth Recv() means that the previous n - 1 PartialResultSets
-// has already been returned to caller or queued, if no error happened.
-func (sr *sReceiver) waitn(n int) error {
- for i := 0; i < n; i++ {
- select {
- case <-sr.c:
- case <-time.After(10 * time.Second):
- return fmt.Errorf("timeout in waiting for %v-th Recv()", i+1)
- }
- }
- return nil
-}
-
-// Test the handling of resumableStreamDecoder.bytesBetweenResumeTokens.
-func TestQueueBytes(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- defer ms.Stop()
- opts := []grpc.DialOption{
- grpc.WithInsecure(),
- }
- cc, err := grpc.Dial(ms.Addr(), opts...)
- if err != nil {
- t.Fatalf("Dial(%q) = %v", ms.Addr(), err)
- }
- defer cc.Close()
- mc := sppb.NewSpannerClient(cc)
- sr := &sReceiver{
- c: make(chan int, 1000), // will never block in this test
- }
- wantQueueBytes := 0
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- r := newResumableStreamDecoder(
- ctx,
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- r, err := mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
- sr.rpcReceiver = r
- return sr, err
- },
- )
- go func() {
- for r.next() {
- }
- }()
- // Let server send maxBuffers / 2 rows.
- for i := 0; i < maxBuffers/2; i++ {
- wantQueueBytes += proto.Size(&sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- ms.AddMsg(nil, false)
- }
- if err := sr.waitn(maxBuffers/2 + 1); err != nil {
- t.Fatalf("failed to wait for the first %v recv() calls: %v", maxBuffers, err)
- }
- if int32(wantQueueBytes) != r.bytesBetweenResumeTokens {
- t.Errorf("r.bytesBetweenResumeTokens = %v, want %v", r.bytesBetweenResumeTokens, wantQueueBytes)
- }
- // Now send a resume token to drain the queue.
- ms.AddMsg(nil, true)
- // Wait for all rows to be processes.
- if err := sr.waitn(1); err != nil {
- t.Fatalf("failed to wait for rows to be processed: %v", err)
- }
- if r.bytesBetweenResumeTokens != 0 {
- t.Errorf("r.bytesBetweenResumeTokens = %v, want 0", r.bytesBetweenResumeTokens)
- }
- // Let server send maxBuffers - 1 rows.
- wantQueueBytes = 0
- for i := 0; i < maxBuffers-1; i++ {
- wantQueueBytes += proto.Size(&sppb.PartialResultSet{
- Metadata: kvMeta,
- Values: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- ms.AddMsg(nil, false)
- }
- if err := sr.waitn(maxBuffers - 1); err != nil {
- t.Fatalf("failed to wait for %v rows to be processed: %v", maxBuffers-1, err)
- }
- if int32(wantQueueBytes) != r.bytesBetweenResumeTokens {
- t.Errorf("r.bytesBetweenResumeTokens = %v, want 0", r.bytesBetweenResumeTokens)
- }
- // Trigger a state transition: queueingRetryable -> queueingUnretryable.
- ms.AddMsg(nil, false)
- if err := sr.waitn(1); err != nil {
- t.Fatalf("failed to wait for state transition: %v", err)
- }
- if r.bytesBetweenResumeTokens != 0 {
- t.Errorf("r.bytesBetweenResumeTokens = %v, want 0", r.bytesBetweenResumeTokens)
- }
-}
-
-// Verify that client can deal with resume token correctly
-func TestResumeToken(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- opts := []grpc.DialOption{
- grpc.WithInsecure(),
- }
- cc, err := grpc.Dial(ms.Addr(), opts...)
- if err != nil {
- t.Fatalf("Dial(%q) = %v", ms.Addr(), err)
- }
- defer func() {
- ms.Stop()
- cc.Close()
- }()
- mc := sppb.NewSpannerClient(cc)
- sr := &sReceiver{
- c: make(chan int, 1000), // will never block in this test
- }
- rows := []*Row{}
- done := make(chan int)
- streaming := func() {
- // Establish a stream to mock cloud spanner server.
- iter := stream(context.Background(),
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- r, err := mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
- sr.rpcReceiver = r
- return sr, err
- },
- nil,
- func(error) {})
- defer iter.Stop()
- for {
- var row *Row
- row, err = iter.Next()
- if err == iterator.Done {
- err = nil
- break
- }
- if err != nil {
- break
- }
- rows = append(rows, row)
- }
- done <- 1
- }
- go streaming()
- // Server streaming row 0 - 2, only row 1 has resume token.
- // Client will receive row 0 - 2, so it will try receiving for
- // 4 times (the last recv will block), and only row 0 - 1 will
- // be yielded.
- for i := 0; i < 3; i++ {
- if i == 1 {
- ms.AddMsg(nil, true)
- } else {
- ms.AddMsg(nil, false)
- }
- }
- // Wait for 4 receive attempts, as explained above.
- if err = sr.waitn(4); err != nil {
- t.Fatalf("failed to wait for row 0 - 2: %v", err)
- }
- want := []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(0)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(0)}},
- },
- },
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(1)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(1)}},
- },
- },
- }
- if !reflect.DeepEqual(rows, want) {
- t.Errorf("received rows: \n%v\n; but want\n%v\n", rows, want)
- }
- // Inject resumable failure.
- ms.AddMsg(
- grpc.Errorf(codes.Unavailable, "mock server unavailable"),
- false,
- )
- // Test if client detects the resumable failure and retries.
- if err = sr.waitn(1); err != nil {
- t.Fatalf("failed to wait for client to retry: %v", err)
- }
- // Client has resumed the query, now server resend row 2.
- ms.AddMsg(nil, true)
- if err = sr.waitn(1); err != nil {
- t.Fatalf("failed to wait for resending row 2: %v", err)
- }
- // Now client should have received row 0 - 2.
- want = append(want, &Row{
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(2)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(2)}},
- },
- })
- if !reflect.DeepEqual(rows, want) {
- t.Errorf("received rows: \n%v\n, want\n%v\n", rows, want)
- }
- // Sending 3rd - (maxBuffers+1)th rows without resume tokens, client should buffer them.
- for i := 3; i < maxBuffers+2; i++ {
- ms.AddMsg(nil, false)
- }
- if err = sr.waitn(maxBuffers - 1); err != nil {
- t.Fatalf("failed to wait for row 3-%v: %v", maxBuffers+1, err)
- }
- // Received rows should be unchanged.
- if !reflect.DeepEqual(rows, want) {
- t.Errorf("receive rows: \n%v\n, want\n%v\n", rows, want)
- }
- // Send (maxBuffers+2)th row to trigger state change of resumableStreamDecoder:
- // queueingRetryable -> queueingUnretryable
- ms.AddMsg(nil, false)
- if err = sr.waitn(1); err != nil {
- t.Fatalf("failed to wait for row %v: %v", maxBuffers+2, err)
- }
- // Client should yield row 3rd - (maxBuffers+2)th to application. Therefore, application should
- // see row 0 - (maxBuffers+2)th so far.
- for i := 3; i < maxBuffers+3; i++ {
- want = append(want, &Row{
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(i)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(i)}},
- },
- })
- }
- if !reflect.DeepEqual(rows, want) {
- t.Errorf("received rows: \n%v\n; want\n%v\n", rows, want)
- }
- // Inject resumable error, but since resumableStreamDecoder is already at queueingUnretryable
- // state, query will just fail.
- ms.AddMsg(
- grpc.Errorf(codes.Unavailable, "mock server wants some sleep"),
- false,
- )
- select {
- case <-done:
- case <-time.After(10 * time.Second):
- t.Fatalf("timeout in waiting for failed query to return.")
- }
- if wantErr := toSpannerError(grpc.Errorf(codes.Unavailable, "mock server wants some sleep")); !reflect.DeepEqual(err, wantErr) {
- t.Fatalf("stream() returns error: %v, but want error: %v", err, wantErr)
- }
-
- // Reconnect to mock Cloud Spanner.
- rows = []*Row{}
- go streaming()
- // Let server send two rows without resume token.
- for i := maxBuffers + 3; i < maxBuffers+5; i++ {
- ms.AddMsg(nil, false)
- }
- if err = sr.waitn(3); err != nil {
- t.Fatalf("failed to wait for row %v - %v: %v", maxBuffers+3, maxBuffers+5, err)
- }
- if len(rows) > 0 {
- t.Errorf("client received some rows unexpectedly: %v, want nothing", rows)
- }
- // Let server end the query.
- ms.AddMsg(io.EOF, false)
- select {
- case <-done:
- case <-time.After(10 * time.Second):
- t.Fatalf("timeout in waiting for failed query to return")
- }
- if err != nil {
- t.Fatalf("stream() returns unexpected error: %v, but want no error", err)
- }
- // Verify if a normal server side EOF flushes all queued rows.
- want = []*Row{
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(maxBuffers + 3)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(maxBuffers + 3)}},
- },
- },
- {
- fields: kvMeta.RowType.Fields,
- vals: []*proto3.Value{
- {Kind: &proto3.Value_StringValue{StringValue: keyStr(maxBuffers + 4)}},
- {Kind: &proto3.Value_StringValue{StringValue: valStr(maxBuffers + 4)}},
- },
- },
- }
- if !reflect.DeepEqual(rows, want) {
- t.Errorf("received rows: \n%v\n; but want\n%v\n", rows, want)
- }
-}
-
-// Verify that streaming query get retried upon real gRPC server transport failures.
-func TestGrpcReconnect(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- defer ms.Stop()
- cc, err := grpc.Dial(ms.Addr(), grpc.WithInsecure())
- if err != nil {
- t.Fatalf("Dial(%q) = %v", ms.Addr(), err)
- }
- defer cc.Close()
- mc := sppb.NewSpannerClient(cc)
- retry := make(chan int)
- row := make(chan int)
- go func() {
- r := 0
- // Establish a stream to mock cloud spanner server.
- iter := stream(context.Background(),
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- if r > 0 {
- // This RPC attempt is a retry, signal it.
- retry <- r
- }
- r++
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
-
- },
- nil,
- func(error) {})
- defer iter.Stop()
- for {
- _, err = iter.Next()
- if err == iterator.Done {
- err = nil
- break
- }
- if err != nil {
- break
- }
- row <- 0
- }
- }()
- // Add a message and wait for the receipt.
- ms.AddMsg(nil, true)
- select {
- case <-row:
- case <-time.After(10 * time.Second):
- t.Fatalf("expect stream to be established within 10 seconds, but it didn't")
- }
- // Error injection: force server to close all connections.
- ms.Stop()
- // Test to see if client respond to the real RPC failure correctly by
- // retrying RPC.
- select {
- case r, ok := <-retry:
- if ok && r == 1 {
- break
- }
- t.Errorf("retry count = %v, want 1", r)
- case <-time.After(10 * time.Second):
- t.Errorf("client library failed to respond after 10 seconds, aborting")
- return
- }
-}
-
-// Test cancel/timeout for client operations.
-func TestCancelTimeout(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- defer ms.Stop()
- opts := []grpc.DialOption{
- grpc.WithInsecure(),
- }
- cc, err := grpc.Dial(ms.Addr(), opts...)
- defer cc.Close()
- if err != nil {
- t.Fatalf("Dial(%q) = %v", ms.Addr(), err)
- }
- mc := sppb.NewSpannerClient(cc)
- done := make(chan int)
- go func() {
- for {
- ms.AddMsg(nil, true)
- }
- }()
- // Test cancelling query.
- ctx, cancel := context.WithCancel(context.Background())
- go func() {
- // Establish a stream to mock cloud spanner server.
- iter := stream(ctx,
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
- },
- nil,
- func(error) {})
- defer iter.Stop()
- for {
- _, err = iter.Next()
- if err == iterator.Done {
- break
- }
- if err != nil {
- done <- 0
- break
- }
- }
- }()
- cancel()
- select {
- case <-done:
- if ErrCode(err) != codes.Canceled {
- t.Errorf("streaming query is canceled and returns error %v, want error code %v", err, codes.Canceled)
- }
- case <-time.After(1 * time.Second):
- t.Errorf("query doesn't exit timely after being cancelled")
- }
- // Test query timeout.
- ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
- go func() {
- // Establish a stream to mock cloud spanner server.
- iter := stream(ctx,
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
- },
- nil,
- func(error) {})
- defer iter.Stop()
- for {
- _, err = iter.Next()
- if err == iterator.Done {
- err = nil
- break
- }
- if err != nil {
- break
- }
- }
- done <- 0
- }()
- select {
- case <-done:
- if wantErr := codes.DeadlineExceeded; ErrCode(err) != wantErr {
- t.Errorf("streaming query timeout returns error %v, want error code %v", err, wantErr)
- }
- case <-time.After(2 * time.Second):
- t.Errorf("query doesn't timeout as expected")
- }
-}
-
-func TestRowIteratorDo(t *testing.T) {
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- defer ms.Stop()
- cc, err := grpc.Dial(ms.Addr(), grpc.WithInsecure())
- if err != nil {
- t.Fatalf("Dial(%q) = %v", ms.Addr(), err)
- }
- defer cc.Close()
- mc := sppb.NewSpannerClient(cc)
-
- for i := 0; i < 3; i++ {
- ms.AddMsg(nil, false)
- }
- ms.AddMsg(io.EOF, true)
- nRows := 0
- iter := stream(context.Background(),
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
- },
- nil,
- func(error) {})
- err = iter.Do(func(r *Row) error { nRows++; return nil })
- if err != nil {
- t.Errorf("Using Do: %v", err)
- }
- if nRows != 3 {
- t.Errorf("got %d rows, want 3", nRows)
- }
-}
-
-func TestIteratorStopEarly(t *testing.T) {
- ctx := context.Background()
- restore := setMaxBytesBetweenResumeTokens()
- defer restore()
- ms := testutil.NewMockCloudSpanner(t, trxTs)
- ms.Serve()
- defer ms.Stop()
- cc, err := grpc.Dial(ms.Addr(), grpc.WithInsecure())
- if err != nil {
- t.Fatalf("Dial(%q) = %v", ms.Addr(), err)
- }
- defer cc.Close()
- mc := sppb.NewSpannerClient(cc)
-
- ms.AddMsg(nil, false)
- ms.AddMsg(nil, false)
- ms.AddMsg(io.EOF, true)
-
- iter := stream(ctx,
- func(ct context.Context, resumeToken []byte) (streamingReceiver, error) {
- return mc.ExecuteStreamingSql(ct, &sppb.ExecuteSqlRequest{
- Sql: "SELECT t.key key, t.value value FROM t_mock t",
- ResumeToken: resumeToken,
- })
- },
- nil,
- func(error) {})
- _, err = iter.Next()
- if err != nil {
- t.Fatalf("before Stop: %v", err)
- }
- iter.Stop()
- // Stop sets r.err to the FailedPrecondition error "Next called after Stop".
- // Override that here so this test can observe the Canceled error from the stream.
- iter.err = nil
- iter.Next()
- if ErrCode(iter.streamd.lastErr()) != codes.Canceled {
- t.Errorf("after Stop: got %v, wanted Canceled", err)
- }
-}
-
-func TestIteratorWithError(t *testing.T) {
- injected := errors.New("Failed iterator")
- iter := RowIterator{err: injected}
- defer iter.Stop()
- if _, err := iter.Next(); err != injected {
- t.Fatalf("Expected error: %v, got %v", injected, err)
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/retry.go b/vendor/cloud.google.com/go/spanner/retry.go
deleted file mode 100644
index 6d535ef41..000000000
--- a/vendor/cloud.google.com/go/spanner/retry.go
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
- "strings"
- "time"
-
- "github.com/golang/protobuf/proto"
- "github.com/golang/protobuf/ptypes"
- "golang.org/x/net/context"
- edpb "google.golang.org/genproto/googleapis/rpc/errdetails"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-const (
- retryInfoKey = "google.rpc.retryinfo-bin"
-)
-
-// errRetry returns an unavailable error under error namespace EsOther. It is a
-// generic retryable error that is used to mask and recover unretryable errors
-// in a retry loop.
-func errRetry(err error) error {
- if se, ok := err.(*Error); ok {
- return &Error{codes.Unavailable, fmt.Sprintf("generic Cloud Spanner retryable error: { %v }", se.Error()), se.trailers}
- }
- return spannerErrorf(codes.Unavailable, "generic Cloud Spanner retryable error: { %v }", err.Error())
-}
-
-// isErrorClosing reports whether the error is generated by gRPC layer talking to a closed server.
-func isErrorClosing(err error) bool {
- if err == nil {
- return false
- }
- if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "transport is closing") {
- // Handle the case when connection is closed unexpectedly.
- // TODO: once gRPC is able to categorize
- // this as retryable error, we should stop parsing the
- // error message here.
- return true
- }
- return false
-}
-
-// isErrorRST reports whether the error is generated by gRPC client receiving a RST frame from server.
-func isErrorRST(err error) bool {
- if err == nil {
- return false
- }
- if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "stream terminated by RST_STREAM") {
- // TODO: once gRPC is able to categorize this error as "go away" or "retryable",
- // we should stop parsing the error message.
- return true
- }
- return false
-}
-
-// isErrorUnexpectedEOF returns true if error is generated by gRPC layer
-// receiving io.EOF unexpectedly.
-func isErrorUnexpectedEOF(err error) bool {
- if err == nil {
- return false
- }
- if ErrCode(err) == codes.Unknown && strings.Contains(ErrDesc(err), "unexpected EOF") {
- // Unexpected EOF is an transport layer issue that
- // could be recovered by retries. The most likely
- // scenario is a flaky RecvMsg() call due to network
- // issues.
- // TODO: once gRPC is able to categorize
- // this as retryable error, we should stop parsing the
- // error message here.
- return true
- }
- return false
-}
-
-// isErrorUnavailable returns true if the error is about server being unavailable.
-func isErrorUnavailable(err error) bool {
- if err == nil {
- return false
- }
- if ErrCode(err) == codes.Unavailable {
- return true
- }
- return false
-}
-
-// isRetryable returns true if the Cloud Spanner error being checked is a retryable error.
-func isRetryable(err error) bool {
- if isErrorClosing(err) {
- return true
- }
- if isErrorUnexpectedEOF(err) {
- return true
- }
- if isErrorRST(err) {
- return true
- }
- if isErrorUnavailable(err) {
- return true
- }
- return false
-}
-
-// errContextCanceled returns *spanner.Error for canceled context.
-func errContextCanceled(ctx context.Context, lastErr error) error {
- if ctx.Err() == context.DeadlineExceeded {
- return spannerErrorf(codes.DeadlineExceeded, "%v, lastErr is <%v>", ctx.Err(), lastErr)
- }
- return spannerErrorf(codes.Canceled, "%v, lastErr is <%v>", ctx.Err(), lastErr)
-}
-
-// extractRetryDelay extracts retry backoff if present.
-func extractRetryDelay(err error) (time.Duration, bool) {
- trailers := errTrailers(err)
- if trailers == nil {
- return 0, false
- }
- elem, ok := trailers[retryInfoKey]
- if !ok || len(elem) <= 0 {
- return 0, false
- }
- _, b, err := metadata.DecodeKeyValue(retryInfoKey, elem[0])
- if err != nil {
- return 0, false
- }
- var retryInfo edpb.RetryInfo
- if proto.Unmarshal([]byte(b), &retryInfo) != nil {
- return 0, false
- }
- delay, err := ptypes.Duration(retryInfo.RetryDelay)
- if err != nil {
- return 0, false
- }
- return delay, true
-}
-
-// runRetryable keeps attempting to run f until one of the following happens:
-// 1) f returns nil error or an unretryable error;
-// 2) context is cancelled or timeout.
-// TODO: consider using https://github.com/googleapis/gax-go once it
-// becomes available internally.
-func runRetryable(ctx context.Context, f func(context.Context) error) error {
- var funcErr error
- retryCount := 0
- for {
- select {
- case <-ctx.Done():
- // Do context check here so that even f() failed to do
- // so (for example, gRPC implementation bug), the loop
- // can still have a chance to exit as expected.
- return errContextCanceled(ctx, funcErr)
- default:
- }
- funcErr = f(ctx)
- if funcErr == nil {
- return nil
- }
- if isRetryable(funcErr) {
- // Error is retryable, do exponential backoff and continue.
- b, ok := extractRetryDelay(funcErr)
- if !ok {
- b = defaultBackoff.delay(retryCount)
- }
- select {
- case <-ctx.Done():
- return errContextCanceled(ctx, funcErr)
- case <-time.After(b):
- }
- retryCount++
- continue
- }
- // Error isn't retryable / no error, return immediately.
- return toSpannerError(funcErr)
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/retry_test.go b/vendor/cloud.google.com/go/spanner/retry_test.go
deleted file mode 100644
index 49c5051ab..000000000
--- a/vendor/cloud.google.com/go/spanner/retry_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "errors"
- "fmt"
- "reflect"
- "testing"
- "time"
-
- "github.com/golang/protobuf/proto"
- "github.com/golang/protobuf/ptypes"
- "golang.org/x/net/context"
- edpb "google.golang.org/genproto/googleapis/rpc/errdetails"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-// Test if runRetryable loop deals with various errors correctly.
-func TestRetry(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- responses := []error{
- grpc.Errorf(codes.Internal, "transport is closing"),
- grpc.Errorf(codes.Unknown, "unexpected EOF"),
- grpc.Errorf(codes.Internal, "stream terminated by RST_STREAM with error code: 2"),
- grpc.Errorf(codes.Unavailable, "service is currently unavailable"),
- errRetry(fmt.Errorf("just retry it")),
- }
- err := runRetryable(context.Background(), func(ct context.Context) error {
- var r error
- if len(responses) > 0 {
- r = responses[0]
- responses = responses[1:]
- }
- return r
- })
- if err != nil {
- t.Errorf("runRetryable should be able to survive all retryable errors, but it returns %v", err)
- }
- // Unretryable errors
- injErr := errors.New("this is unretryable")
- err = runRetryable(context.Background(), func(ct context.Context) error {
- return injErr
- })
- if wantErr := toSpannerError(injErr); !reflect.DeepEqual(err, wantErr) {
- t.Errorf("runRetryable returns error %v, want %v", err, wantErr)
- }
- // Timeout
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
- retryErr := errRetry(fmt.Errorf("still retrying"))
- err = runRetryable(ctx, func(ct context.Context) error {
- // Expect to trigger timeout in retryable runner after 10 executions.
- <-time.After(100 * time.Millisecond)
- // Let retryable runner to retry so that timeout will eventually happen.
- return retryErr
- })
- // Check error code and error message
- if wantErrCode, wantErr := codes.DeadlineExceeded, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !reflect.DeepEqual(err, wantErr) {
- t.Errorf("<err code, err>=\n<%v, %v>, want:\n<%v, %v>", ErrCode(err), err, wantErrCode, wantErr)
- }
- // Cancellation
- ctx, cancel = context.WithCancel(context.Background())
- retries := 3
- retryErr = errRetry(fmt.Errorf("retry before cancel"))
- err = runRetryable(ctx, func(ct context.Context) error {
- retries--
- if retries == 0 {
- cancel()
- }
- return retryErr
- })
- // Check error code, error message, retry count
- if wantErrCode, wantErr := codes.Canceled, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !reflect.DeepEqual(err, wantErr) || retries != 0 {
- t.Errorf("<err code, err, retries>=\n<%v, %v, %v>, want:\n<%v, %v, %v>", ErrCode(err), err, retries, wantErrCode, wantErr, 0)
- }
-}
-
-func TestRetryInfo(t *testing.T) {
- b, _ := proto.Marshal(&edpb.RetryInfo{
- RetryDelay: ptypes.DurationProto(time.Second),
- })
- trailers := map[string]string{
- retryInfoKey: string(b),
- }
- gotDelay, ok := extractRetryDelay(errRetry(toSpannerErrorWithMetadata(grpc.Errorf(codes.Aborted, ""), metadata.New(trailers))))
- if !ok || !reflect.DeepEqual(time.Second, gotDelay) {
- t.Errorf("<ok, retryDelay> = <%t, %v>, want <true, %v>", ok, gotDelay, time.Second)
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/row.go b/vendor/cloud.google.com/go/spanner/row.go
deleted file mode 100644
index e8f30103d..000000000
--- a/vendor/cloud.google.com/go/spanner/row.go
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
- "reflect"
-
- proto3 "github.com/golang/protobuf/ptypes/struct"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc/codes"
-)
-
-// A Row is a view of a row of data produced by a Cloud Spanner read.
-//
-// A row consists of a number of columns; the number depends on the columns
-// used to construct the read.
-//
-// The column values can be accessed by index, where the indices are with
-// respect to the columns. For instance, if the read specified
-// []string{"photo_id", "caption", "metadata"}, then each row will
-// contain three columns: the 0th column corresponds to "photo_id", the
-// 1st column corresponds to "caption", etc.
-//
-// Column values are decoded by using one of the Column, ColumnByName, or
-// Columns methods. The valid values passed to these methods depend on the
-// column type. For example:
-//
-// var photoID int64
-// err := row.Column(0, &photoID) // Decode column 0 as an integer.
-//
-// var caption string
-// err := row.Column(1, &caption) // Decode column 1 as a string.
-//
-// // The above two operations at once.
-// err := row.Columns(&photoID, &caption)
-//
-// Supported types and their corresponding Cloud Spanner column type(s) are:
-//
-// *string(not NULL), *NullString - STRING
-// *[]NullString - STRING ARRAY
-// *[]byte - BYTES
-// *[][]byte - BYTES ARRAY
-// *int64(not NULL), *NullInt64 - INT64
-// *[]NullInt64 - INT64 ARRAY
-// *bool(not NULL), *NullBool - BOOL
-// *[]NullBool - BOOL ARRAY
-// *float64(not NULL), *NullFloat64 - FLOAT64
-// *[]NullFloat64 - FLOAT64 ARRAY
-// *time.Time(not NULL), *NullTime - TIMESTAMP
-// *[]NullTime - TIMESTAMP ARRAY
-// *Date(not NULL), *NullDate - DATE
-// *[]NullDate - DATE ARRAY
-// *[]*some_go_struct, *[]NullRow - STRUCT ARRAY
-// *GenericColumnValue - any Cloud Spanner type
-//
-// For TIMESTAMP columns, returned time.Time object will be in UTC.
-//
-// To fetch an array of BYTES, pass a *[][]byte. To fetch an array of
-// (sub)rows, pass a *[]spanner.NullRow or a *[]*some_go_struct where
-// some_go_struct holds all information of the subrow, see spannr.Row.ToStruct
-// for the mapping between Cloud Spanner row and Go struct. To fetch an array of
-// other types, pass a *[]spanner.Null* type of the appropriate type. Use
-// *GenericColumnValue when you don't know in advance what column type to
-// expect.
-//
-// Row decodes the row contents lazily; as a result, each call to a getter has
-// a chance of returning an error.
-//
-// A column value may be NULL if the corresponding value is not present in
-// Cloud Spanner. The spanner.Null* types (spanner.NullInt64 et al.) allow fetching
-// values that may be null. A NULL BYTES can be fetched into a *[]byte as nil.
-// It is an error to fetch a NULL value into any other type.
-type Row struct {
- fields []*sppb.StructType_Field
- vals []*proto3.Value // keep decoded for now
-}
-
-// errNamesValuesMismatch returns error for when columnNames count is not equal
-// to columnValues count.
-func errNamesValuesMismatch(columnNames []string, columnValues []interface{}) error {
- return spannerErrorf(codes.FailedPrecondition,
- "different number of names(%v) and values(%v)", len(columnNames), len(columnValues))
-}
-
-// NewRow returns a Row containing the supplied data. This can be useful for
-// mocking Cloud Spanner Read and Query responses for unit testing.
-func NewRow(columnNames []string, columnValues []interface{}) (*Row, error) {
- if len(columnValues) != len(columnNames) {
- return nil, errNamesValuesMismatch(columnNames, columnValues)
- }
- r := Row{
- fields: make([]*sppb.StructType_Field, len(columnValues)),
- vals: make([]*proto3.Value, len(columnValues)),
- }
- for i := range columnValues {
- val, typ, err := encodeValue(columnValues[i])
- if err != nil {
- return nil, err
- }
- r.fields[i] = &sppb.StructType_Field{
- Name: columnNames[i],
- Type: typ,
- }
- r.vals[i] = val
- }
- return &r, nil
-}
-
-// Size is the number of columns in the row.
-func (r *Row) Size() int {
- return len(r.fields)
-}
-
-// ColumnName returns the name of column i, or empty string for invalid column.
-func (r *Row) ColumnName(i int) string {
- if i < 0 || i >= len(r.fields) {
- return ""
- }
- return r.fields[i].Name
-}
-
-// ColumnIndex returns the index of the column with the given name. The
-// comparison is case-sensitive.
-func (r *Row) ColumnIndex(name string) (int, error) {
- found := false
- var index int
- if len(r.vals) != len(r.fields) {
- return 0, errFieldsMismatchVals(r)
- }
- for i, f := range r.fields {
- if f == nil {
- return 0, errNilColType(i)
- }
- if name == f.Name {
- if found {
- return 0, errDupColName(name)
- }
- found = true
- index = i
- }
- }
- if !found {
- return 0, errColNotFound(name)
- }
- return index, nil
-}
-
-// ColumnNames returns all column names of the row.
-func (r *Row) ColumnNames() []string {
- var n []string
- for _, c := range r.fields {
- n = append(n, c.Name)
- }
- return n
-}
-
-// errColIdxOutOfRange returns error for requested column index is out of the
-// range of the target Row's columns.
-func errColIdxOutOfRange(i int, r *Row) error {
- return spannerErrorf(codes.OutOfRange, "column index %d out of range [0,%d)", i, len(r.vals))
-}
-
-// errDecodeColumn returns error for not being able to decode a indexed column.
-func errDecodeColumn(i int, err error) error {
- if err == nil {
- return nil
- }
- se, ok := toSpannerError(err).(*Error)
- if !ok {
- return spannerErrorf(codes.InvalidArgument, "failed to decode column %v, error = <%v>", i, err)
- }
- se.decorate(fmt.Sprintf("failed to decode column %v", i))
- return se
-}
-
-// errFieldsMismatchVals returns error for field count isn't equal to value count in a Row.
-func errFieldsMismatchVals(r *Row) error {
- return spannerErrorf(codes.FailedPrecondition, "row has different number of fields(%v) and values(%v)",
- len(r.fields), len(r.vals))
-}
-
-// errNilColType returns error for column type for column i being nil in the row.
-func errNilColType(i int) error {
- return spannerErrorf(codes.FailedPrecondition, "column(%v)'s type is nil", i)
-}
-
-// Column fetches the value from the ith column, decoding it into ptr.
-// See the Row documentation for the list of acceptable argument types.
-// see Client.ReadWriteTransaction for an example.
-func (r *Row) Column(i int, ptr interface{}) error {
- if len(r.vals) != len(r.fields) {
- return errFieldsMismatchVals(r)
- }
- if i < 0 || i >= len(r.fields) {
- return errColIdxOutOfRange(i, r)
- }
- if r.fields[i] == nil {
- return errNilColType(i)
- }
- if err := decodeValue(r.vals[i], r.fields[i].Type, ptr); err != nil {
- return errDecodeColumn(i, err)
- }
- return nil
-}
-
-// errDupColName returns error for duplicated column name in the same row.
-func errDupColName(n string) error {
- return spannerErrorf(codes.FailedPrecondition, "ambiguous column name %q", n)
-}
-
-// errColNotFound returns error for not being able to find a named column.
-func errColNotFound(n string) error {
- return spannerErrorf(codes.NotFound, "column %q not found", n)
-}
-
-// ColumnByName fetches the value from the named column, decoding it into ptr.
-// See the Row documentation for the list of acceptable argument types.
-func (r *Row) ColumnByName(name string, ptr interface{}) error {
- index, err := r.ColumnIndex(name)
- if err != nil {
- return err
- }
- return r.Column(index, ptr)
-}
-
-// errNumOfColValue returns error for providing wrong number of values to Columns.
-func errNumOfColValue(n int, r *Row) error {
- return spannerErrorf(codes.InvalidArgument,
- "Columns(): number of arguments (%d) does not match row size (%d)", n, len(r.vals))
-}
-
-// Columns fetches all the columns in the row at once.
-//
-// The value of the kth column will be decoded into the kth argument to
-// Columns. See above for the list of acceptable argument types. The number of
-// arguments must be equal to the number of columns. Pass nil to specify that a
-// column should be ignored.
-func (r *Row) Columns(ptrs ...interface{}) error {
- if len(ptrs) != len(r.vals) {
- return errNumOfColValue(len(ptrs), r)
- }
- if len(r.vals) != len(r.fields) {
- return errFieldsMismatchVals(r)
- }
- for i, p := range ptrs {
- if p == nil {
- continue
- }
- if err := r.Column(i, p); err != nil {
- return err
- }
- }
- return nil
-}
-
-// errToStructArgType returns error for p not having the correct data type(pointer to Go struct) to
-// be the argument of Row.ToStruct.
-func errToStructArgType(p interface{}) error {
- return spannerErrorf(codes.InvalidArgument, "ToStruct(): type %T is not a valid pointer to Go struct", p)
-}
-
-// ToStruct fetches the columns in a row into the fields of a struct.
-// The rules for mapping a row's columns into a struct's exported fields
-// are as the following:
-// 1. If a field has a `spanner: "column_name"` tag, then decode column
-// 'column_name' into the field. A special case is the `spanner: "-"`
-// tag, which instructs ToStruct to ignore the field during decoding.
-// 2. Otherwise, if the name of a field matches the name of a column (ignoring case),
-// decode the column into the field.
-//
-// The fields of the destination struct can be of any type that is acceptable
-// to (*spanner.Row).Column.
-//
-// Slice and pointer fields will be set to nil if the source column
-// is NULL, and a non-nil value if the column is not NULL. To decode NULL
-// values of other types, use one of the spanner.Null* as the type of the
-// destination field.
-func (r *Row) ToStruct(p interface{}) error {
- // Check if p is a pointer to a struct
- if t := reflect.TypeOf(p); t == nil || t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
- return errToStructArgType(p)
- }
- if len(r.vals) != len(r.fields) {
- return errFieldsMismatchVals(r)
- }
- // Call decodeStruct directly to decode the row as a typed proto.ListValue.
- return decodeStruct(
- &sppb.StructType{Fields: r.fields},
- &proto3.ListValue{Values: r.vals},
- p,
- )
-}
diff --git a/vendor/cloud.google.com/go/spanner/row_test.go b/vendor/cloud.google.com/go/spanner/row_test.go
deleted file mode 100644
index 2120421ac..000000000
--- a/vendor/cloud.google.com/go/spanner/row_test.go
+++ /dev/null
@@ -1,1775 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "encoding/base64"
- "reflect"
- "strconv"
- "strings"
- "testing"
- "time"
-
- "cloud.google.com/go/civil"
- proto "github.com/golang/protobuf/proto"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-var (
- tm = time.Date(2016, 11, 15, 0, 0, 0, 0, time.UTC)
- dt, _ = civil.ParseDate("2016-11-15")
- // row contains a column for each unique Cloud Spanner type.
- row = Row{
- []*sppb.StructType_Field{
- // STRING / STRING ARRAY
- {"STRING", stringType()},
- {"NULL_STRING", stringType()},
- {"STRING_ARRAY", listType(stringType())},
- {"NULL_STRING_ARRAY", listType(stringType())},
- // BYTES / BYTES ARRAY
- {"BYTES", bytesType()},
- {"NULL_BYTES", bytesType()},
- {"BYTES_ARRAY", listType(bytesType())},
- {"NULL_BYTES_ARRAY", listType(bytesType())},
- // INT64 / INT64 ARRAY
- {"INT64", intType()},
- {"NULL_INT64", intType()},
- {"INT64_ARRAY", listType(intType())},
- {"NULL_INT64_ARRAY", listType(intType())},
- // BOOL / BOOL ARRAY
- {"BOOL", boolType()},
- {"NULL_BOOL", boolType()},
- {"BOOL_ARRAY", listType(boolType())},
- {"NULL_BOOL_ARRAY", listType(boolType())},
- // FLOAT64 / FLOAT64 ARRAY
- {"FLOAT64", floatType()},
- {"NULL_FLOAT64", floatType()},
- {"FLOAT64_ARRAY", listType(floatType())},
- {"NULL_FLOAT64_ARRAY", listType(floatType())},
- // TIMESTAMP / TIMESTAMP ARRAY
- {"TIMESTAMP", timeType()},
- {"NULL_TIMESTAMP", timeType()},
- {"TIMESTAMP_ARRAY", listType(timeType())},
- {"NULL_TIMESTAMP_ARRAY", listType(timeType())},
- // DATE / DATE ARRAY
- {"DATE", dateType()},
- {"NULL_DATE", dateType()},
- {"DATE_ARRAY", listType(dateType())},
- {"NULL_DATE_ARRAY", listType(dateType())},
-
- // STRUCT ARRAY
- {
- "STRUCT_ARRAY",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- {
- "NULL_STRUCT_ARRAY",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{
- // STRING / STRING ARRAY
- stringProto("value"),
- nullProto(),
- listProto(stringProto("value1"), nullProto(), stringProto("value3")),
- nullProto(),
- // BYTES / BYTES ARRAY
- bytesProto([]byte("value")),
- nullProto(),
- listProto(bytesProto([]byte("value1")), nullProto(), bytesProto([]byte("value3"))),
- nullProto(),
- // INT64 / INT64 ARRAY
- intProto(17),
- nullProto(),
- listProto(intProto(1), intProto(2), nullProto()),
- nullProto(),
- // BOOL / BOOL ARRAY
- boolProto(true),
- nullProto(),
- listProto(nullProto(), boolProto(true), boolProto(false)),
- nullProto(),
- // FLOAT64 / FLOAT64 ARRAY
- floatProto(1.7),
- nullProto(),
- listProto(nullProto(), nullProto(), floatProto(1.7)),
- nullProto(),
- // TIMESTAMP / TIMESTAMP ARRAY
- timeProto(tm),
- nullProto(),
- listProto(nullProto(), timeProto(tm)),
- nullProto(),
- // DATE / DATE ARRAY
- dateProto(dt),
- nullProto(),
- listProto(nullProto(), dateProto(dt)),
- nullProto(),
- // STRUCT ARRAY
- listProto(
- nullProto(),
- listProto(intProto(3), floatProto(33.3), stringProto("three")),
- nullProto(),
- ),
- nullProto(),
- },
- }
-)
-
-// Test helpers for getting column values.
-func TestColumnValues(t *testing.T) {
- vals := []interface{}{}
- wantVals := []interface{}{}
- // Test getting column values.
- for i, wants := range [][]interface{}{
- // STRING / STRING ARRAY
- {"value", NullString{"value", true}},
- {NullString{}},
- {[]NullString{{"value1", true}, {}, {"value3", true}}},
- {[]NullString(nil)},
- // BYTES / BYTES ARRAY
- {[]byte("value")},
- {[]byte(nil)},
- {[][]byte{[]byte("value1"), nil, []byte("value3")}},
- {[][]byte(nil)},
- // INT64 / INT64 ARRAY
- {int64(17), NullInt64{17, true}},
- {NullInt64{}},
- {[]NullInt64{{1, true}, {2, true}, {}}},
- {[]NullInt64(nil)},
- // BOOL / BOOL ARRAY
- {true, NullBool{true, true}},
- {NullBool{}},
- {[]NullBool{{}, {true, true}, {false, true}}},
- {[]NullBool(nil)},
- // FLOAT64 / FLOAT64 ARRAY
- {1.7, NullFloat64{1.7, true}},
- {NullFloat64{}},
- {[]NullFloat64{{}, {}, {1.7, true}}},
- {[]NullFloat64(nil)},
- // TIMESTAMP / TIMESTAMP ARRAY
- {tm, NullTime{tm, true}},
- {NullTime{}},
- {[]NullTime{{}, {tm, true}}},
- {[]NullTime(nil)},
- // DATE / DATE ARRAY
- {dt, NullDate{dt, true}},
- {NullDate{}},
- {[]NullDate{{}, {dt, true}}},
- {[]NullDate(nil)},
- // STRUCT ARRAY
- {
- []*struct {
- Col1 NullInt64
- Col2 NullFloat64
- Col3 string
- }{
- nil,
- &struct {
- Col1 NullInt64
- Col2 NullFloat64
- Col3 string
- }{
- NullInt64{3, true},
- NullFloat64{33.3, true},
- "three",
- },
- nil,
- },
- []NullRow{
- {},
- {
- Row: Row{
- fields: []*sppb.StructType_Field{
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- },
- vals: []*proto3.Value{
- intProto(3),
- floatProto(33.3),
- stringProto("three"),
- },
- },
- Valid: true,
- },
- {},
- },
- },
- {
- []*struct {
- Col1 NullInt64
- Col2 NullFloat64
- Col3 string
- }(nil),
- []NullRow(nil),
- },
- } {
- for j, want := range wants {
- // Prepare Value vector to test Row.Columns.
- if j == 0 {
- vals = append(vals, reflect.New(reflect.TypeOf(want)).Interface())
- wantVals = append(wantVals, want)
- }
- // Column
- gotp := reflect.New(reflect.TypeOf(want))
- err := row.Column(i, gotp.Interface())
- if err != nil {
- t.Errorf("\t row.Column(%v, %T) returns error: %v, want nil", i, gotp.Interface(), err)
- }
- if got := reflect.Indirect(gotp).Interface(); !reflect.DeepEqual(got, want) {
- t.Errorf("\t row.Column(%v, %T) retrives %v, want %v", i, gotp.Interface(), got, want)
- }
- // ColumnByName
- gotp = reflect.New(reflect.TypeOf(want))
- err = row.ColumnByName(row.fields[i].Name, gotp.Interface())
- if err != nil {
- t.Errorf("\t row.ColumnByName(%v, %T) returns error: %v, want nil", row.fields[i].Name, gotp.Interface(), err)
- }
- if got := reflect.Indirect(gotp).Interface(); !reflect.DeepEqual(got, want) {
- t.Errorf("\t row.ColumnByName(%v, %T) retrives %v, want %v", row.fields[i].Name, gotp.Interface(), got, want)
- }
- }
- }
- // Test Row.Columns.
- if err := row.Columns(vals...); err != nil {
- t.Errorf("row.Columns() returns error: %v, want nil", err)
- }
- for i, want := range wantVals {
- if got := reflect.Indirect(reflect.ValueOf(vals[i])).Interface(); !reflect.DeepEqual(got, want) {
- t.Errorf("\t got %v(%T) for column[%v], want %v(%T)", got, got, row.fields[i].Name, want, want)
- }
- }
-}
-
-// Test decoding into nil destination.
-func TestNilDst(t *testing.T) {
- for i, test := range []struct {
- r *Row
- dst interface{}
- wantErr error
- structDst interface{}
- wantToStructErr error
- }{
- {
- &Row{
- []*sppb.StructType_Field{
- {"Col0", stringType()},
- },
- []*proto3.Value{stringProto("value")},
- },
- nil,
- errDecodeColumn(0, errNilDst(nil)),
- nil,
- errToStructArgType(nil),
- },
- {
- &Row{
- []*sppb.StructType_Field{
- {"Col0", stringType()},
- },
- []*proto3.Value{stringProto("value")},
- },
- (*string)(nil),
- errDecodeColumn(0, errNilDst((*string)(nil))),
- (*struct{ STRING string })(nil),
- errNilDst((*struct{ STRING string })(nil)),
- },
- {
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- ),
- ),
- },
- },
- []*proto3.Value{listProto(
- listProto(intProto(3), floatProto(33.3)),
- )},
- },
- (*[]*struct {
- Col1 int
- Col2 float64
- })(nil),
- errDecodeColumn(0, errNilDst((*[]*struct {
- Col1 int
- Col2 float64
- })(nil))),
- (*struct {
- StructArray []*struct {
- Col1 int
- Col2 float64
- } `spanner:"STRUCT_ARRAY"`
- })(nil),
- errNilDst((*struct {
- StructArray []*struct {
- Col1 int
- Col2 float64
- } `spanner:"STRUCT_ARRAY"`
- })(nil)),
- },
- } {
- if gotErr := test.r.Column(0, test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: test.r.Column() returns error %v, want %v", i, gotErr, test.wantErr)
- }
- if gotErr := test.r.ColumnByName("Col0", test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: test.r.ColumnByName() returns error %v, want %v", i, gotErr, test.wantErr)
- }
- // Row.Columns(T) should return nil on T == nil, otherwise, it should return test.wantErr.
- wantColumnsErr := test.wantErr
- if test.dst == nil {
- wantColumnsErr = nil
- }
- if gotErr := test.r.Columns(test.dst); !reflect.DeepEqual(gotErr, wantColumnsErr) {
- t.Errorf("%v: test.r.Columns() returns error %v, want %v", i, gotErr, wantColumnsErr)
- }
- if gotErr := test.r.ToStruct(test.structDst); !reflect.DeepEqual(gotErr, test.wantToStructErr) {
- t.Errorf("%v: test.r.ToStruct() returns error %v, want %v", i, gotErr, test.wantToStructErr)
- }
- }
-}
-
-// Test decoding NULL columns using Go types that don't support NULL.
-func TestNullTypeErr(t *testing.T) {
- var tm time.Time
- ntoi := func(n string) int {
- for i, f := range row.fields {
- if f.Name == n {
- return i
- }
- }
- t.Errorf("cannot find column name %q in row", n)
- return 0
- }
- for _, test := range []struct {
- colName string
- dst interface{}
- }{
- {
- "NULL_STRING",
- proto.String(""),
- },
- {
- "NULL_INT64",
- proto.Int64(0),
- },
- {
- "NULL_BOOL",
- proto.Bool(false),
- },
- {
- "NULL_FLOAT64",
- proto.Float64(0.0),
- },
- {
- "NULL_TIMESTAMP",
- &tm,
- },
- {
- "NULL_DATE",
- &dt,
- },
- } {
- wantErr := errDecodeColumn(ntoi(test.colName), errDstNotForNull(test.dst))
- if gotErr := row.ColumnByName(test.colName, test.dst); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("row.ColumnByName(%v) returns error %v, want %v", test.colName, gotErr, wantErr)
- }
- }
-}
-
-// Test using wrong destination type in column decoders.
-func TestColumnTypeErr(t *testing.T) {
- // badDst cannot hold any of the column values.
- badDst := &struct{}{}
- for i, f := range row.fields { // For each of the columns, try to decode it into badDst.
- tc := f.Type.Code
- isArray := strings.Contains(f.Name, "ARRAY")
- if isArray {
- tc = f.Type.ArrayElementType.Code
- }
- wantErr := errDecodeColumn(i, errTypeMismatch(tc, isArray, badDst))
- if gotErr := row.Column(i, badDst); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("Column(%v): decoding into destination with wrong type %T returns error %v, want %v",
- i, badDst, gotErr, wantErr)
- }
- if gotErr := row.ColumnByName(f.Name, badDst); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("ColumnByName(%v): decoding into destination with wrong type %T returns error %v, want %v",
- f.Name, badDst, gotErr, wantErr)
- }
- }
- wantErr := errDecodeColumn(1, errTypeMismatch(sppb.TypeCode_STRING, false, badDst))
- // badDst is used to receive column 1.
- vals := []interface{}{nil, badDst} // Row.Column() is expected to fail at column 1.
- // Skip decoding the rest columns by providing nils as the destinations.
- for i := 2; i < len(row.fields); i++ {
- vals = append(vals, nil)
- }
- if gotErr := row.Columns(vals...); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("Columns(): decoding column 1 with wrong type %T returns error %v, want %v",
- badDst, gotErr, wantErr)
- }
-}
-
-// Test the handling of invalid column decoding requests which cannot be mapped to correct column(s).
-func TestInvalidColumnRequest(t *testing.T) {
- for _, test := range []struct {
- desc string
- f func() error
- wantErr error
- }{
- {
- "Request column index is out of range",
- func() error {
- return row.Column(10000, &struct{}{})
- },
- errColIdxOutOfRange(10000, &row),
- },
- {
- "Cannot find the named column",
- func() error {
- return row.ColumnByName("string", &struct{}{})
- },
- errColNotFound("string"),
- },
- {
- "Not enough arguments to call row.Columns()",
- func() error {
- return row.Columns(nil, nil)
- },
- errNumOfColValue(2, &row),
- },
- {
- "Call ColumnByName on row with duplicated column names",
- func() error {
- var s string
- r := &Row{
- []*sppb.StructType_Field{
- {"Val", stringType()},
- {"Val", stringType()},
- },
- []*proto3.Value{stringProto("value1"), stringProto("value2")},
- }
- return r.ColumnByName("Val", &s)
- },
- errDupColName("Val"),
- },
- {
- "Call ToStruct on row with duplicated column names",
- func() error {
- s := &struct {
- Val string
- }{}
- r := &Row{
- []*sppb.StructType_Field{
- {"Val", stringType()},
- {"Val", stringType()},
- },
- []*proto3.Value{stringProto("value1"), stringProto("value2")},
- }
- return r.ToStruct(s)
- },
- errDupSpannerField("Val", &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- {"Val", stringType()},
- {"Val", stringType()},
- },
- }),
- },
- {
- "Call ToStruct on a row with unnamed field",
- func() error {
- s := &struct {
- Val string
- }{}
- r := &Row{
- []*sppb.StructType_Field{
- {"", stringType()},
- },
- []*proto3.Value{stringProto("value1")},
- }
- return r.ToStruct(s)
- },
- errUnnamedField(&sppb.StructType{Fields: []*sppb.StructType_Field{{"", stringType()}}}, 0),
- },
- } {
- if gotErr := test.f(); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: test.f() returns error %v, want %v", test.desc, gotErr, test.wantErr)
- }
- }
-}
-
-// Test decoding the row with row.ToStruct into an invalid destination.
-func TestToStructInvalidDst(t *testing.T) {
- for _, test := range []struct {
- desc string
- dst interface{}
- wantErr error
- }{
- {
- "Decode row as STRUCT into int32",
- proto.Int(1),
- errToStructArgType(proto.Int(1)),
- },
- {
- "Decode row as STRUCT to nil Go struct",
- (*struct{})(nil),
- errNilDst((*struct{})(nil)),
- },
- {
- "Decode row as STRUCT to Go struct with duplicated fields for the PK column",
- &struct {
- PK1 string `spanner:"STRING"`
- PK2 string `spanner:"STRING"`
- }{},
- errNoOrDupGoField(&struct {
- PK1 string `spanner:"STRING"`
- PK2 string `spanner:"STRING"`
- }{}, "STRING"),
- },
- {
- "Decode row as STRUCT to Go struct with no field for the PK column",
- &struct {
- PK1 string `spanner:"_STRING"`
- }{},
- errNoOrDupGoField(&struct {
- PK1 string `spanner:"_STRING"`
- }{}, "STRING"),
- },
- {
- "Decode row as STRUCT to Go struct with wrong type for the PK column",
- &struct {
- PK1 int64 `spanner:"STRING"`
- }{},
- errDecodeStructField(&sppb.StructType{Fields: row.fields}, "STRING",
- errTypeMismatch(sppb.TypeCode_STRING, false, proto.Int64(0))),
- },
- } {
- if gotErr := row.ToStruct(test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: decoding:\ngot %v\nwant %v", test.desc, gotErr, test.wantErr)
- }
- }
-}
-
-// Test decoding a broken row.
-func TestBrokenRow(t *testing.T) {
- for i, test := range []struct {
- row *Row
- dst interface{}
- wantErr error
- }{
- {
- // A row with no field.
- &Row{
- []*sppb.StructType_Field{},
- []*proto3.Value{stringProto("value")},
- },
- &NullString{"value", true},
- errFieldsMismatchVals(&Row{
- []*sppb.StructType_Field{},
- []*proto3.Value{stringProto("value")},
- }),
- },
- {
- // A row with nil field.
- &Row{
- []*sppb.StructType_Field{nil},
- []*proto3.Value{stringProto("value")},
- },
- &NullString{"value", true},
- errNilColType(0),
- },
- {
- // Field is not nil, but its type is nil.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- nil,
- },
- },
- []*proto3.Value{listProto(stringProto("value1"), stringProto("value2"))},
- },
- &[]NullString{},
- errDecodeColumn(0, errNilSpannerType()),
- },
- {
- // Field is not nil, field type is not nil, but it is an array and its array element type is nil.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- &sppb.Type{
- Code: sppb.TypeCode_ARRAY,
- },
- },
- },
- []*proto3.Value{listProto(stringProto("value1"), stringProto("value2"))},
- },
- &[]NullString{},
- errDecodeColumn(0, errNilArrElemType(&sppb.Type{Code: sppb.TypeCode_ARRAY})),
- },
- {
- // Field specifies valid type, value is nil.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- intType(),
- },
- },
- []*proto3.Value{nil},
- },
- &NullInt64{1, true},
- errDecodeColumn(0, errNilSrc()),
- },
- {
- // Field specifies INT64 type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- intType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}},
- },
- &NullInt64{1, true},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")),
- },
- {
- // Field specifies INT64 type, but value is for Number type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- intType(),
- },
- },
- []*proto3.Value{floatProto(1.0)},
- },
- &NullInt64{1, true},
- errDecodeColumn(0, errSrcVal(floatProto(1.0), "String")),
- },
- {
- // Field specifies INT64 type, but value is wrongly encoded.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- intType(),
- },
- },
- []*proto3.Value{stringProto("&1")},
- },
- proto.Int64(0),
- errDecodeColumn(0, errBadEncoding(stringProto("&1"), func() error {
- _, err := strconv.ParseInt("&1", 10, 64)
- return err
- }())),
- },
- {
- // Field specifies INT64 type, but value is wrongly encoded.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- intType(),
- },
- },
- []*proto3.Value{stringProto("&1")},
- },
- &NullInt64{},
- errDecodeColumn(0, errBadEncoding(stringProto("&1"), func() error {
- _, err := strconv.ParseInt("&1", 10, 64)
- return err
- }())),
- },
- {
- // Field specifies STRING type, but value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- stringType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}},
- },
- &NullString{"value", true},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")),
- },
- {
- // Field specifies STRING type, but value is for ARRAY type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- stringType(),
- },
- },
- []*proto3.Value{listProto(stringProto("value"))},
- },
- &NullString{"value", true},
- errDecodeColumn(0, errSrcVal(listProto(stringProto("value")), "String")),
- },
- {
- // Field specifies FLOAT64 type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- floatType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_NumberValue)(nil)}},
- },
- &NullFloat64{1.0, true},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_NumberValue)(nil)}, "Number")),
- },
- {
- // Field specifies FLOAT64 type, but value is for BOOL type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- floatType(),
- },
- },
- []*proto3.Value{boolProto(true)},
- },
- &NullFloat64{1.0, true},
- errDecodeColumn(0, errSrcVal(boolProto(true), "Number")),
- },
- {
- // Field specifies FLOAT64 type, but value is wrongly encoded.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- floatType(),
- },
- },
- []*proto3.Value{stringProto("nan")},
- },
- &NullFloat64{},
- errDecodeColumn(0, errUnexpectedNumStr("nan")),
- },
- {
- // Field specifies FLOAT64 type, but value is wrongly encoded.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- floatType(),
- },
- },
- []*proto3.Value{stringProto("nan")},
- },
- proto.Float64(0),
- errDecodeColumn(0, errUnexpectedNumStr("nan")),
- },
- {
- // Field specifies BYTES type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- bytesType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}},
- },
- &[]byte{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")),
- },
- {
- // Field specifies BYTES type, but value is for BOOL type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- bytesType(),
- },
- },
- []*proto3.Value{boolProto(false)},
- },
- &[]byte{},
- errDecodeColumn(0, errSrcVal(boolProto(false), "String")),
- },
- {
- // Field specifies BYTES type, but value is wrongly encoded.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- bytesType(),
- },
- },
- []*proto3.Value{stringProto("&&")},
- },
- &[]byte{},
- errDecodeColumn(0, errBadEncoding(stringProto("&&"), func() error {
- _, err := base64.StdEncoding.DecodeString("&&")
- return err
- }())),
- },
- {
- // Field specifies BOOL type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- boolType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_BoolValue)(nil)}},
- },
- &NullBool{false, true},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_BoolValue)(nil)}, "Bool")),
- },
- {
- // Field specifies BOOL type, but value is for STRING type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- boolType(),
- },
- },
- []*proto3.Value{stringProto("false")},
- },
- &NullBool{false, true},
- errDecodeColumn(0, errSrcVal(stringProto("false"), "Bool")),
- },
- {
- // Field specifies TIMESTAMP type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- timeType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}},
- },
- &NullTime{time.Now(), true},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")),
- },
- {
- // Field specifies TIMESTAMP type, but value is for BOOL type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- timeType(),
- },
- },
- []*proto3.Value{boolProto(false)},
- },
- &NullTime{time.Now(), true},
- errDecodeColumn(0, errSrcVal(boolProto(false), "String")),
- },
- {
- // Field specifies TIMESTAMP type, but value is invalid timestamp.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- timeType(),
- },
- },
- []*proto3.Value{stringProto("junk")},
- },
- &NullTime{time.Now(), true},
- errDecodeColumn(0, errBadEncoding(stringProto("junk"), func() error {
- _, err := time.Parse(time.RFC3339Nano, "junk")
- return err
- }())),
- },
- {
- // Field specifies DATE type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- dateType(),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_StringValue)(nil)}},
- },
- &NullDate{civil.Date{}, true},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_StringValue)(nil)}, "String")),
- },
- {
- // Field specifies DATE type, but value is for BOOL type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- dateType(),
- },
- },
- []*proto3.Value{boolProto(false)},
- },
- &NullDate{civil.Date{}, true},
- errDecodeColumn(0, errSrcVal(boolProto(false), "String")),
- },
- {
- // Field specifies DATE type, but value is invalid timestamp.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- dateType(),
- },
- },
- []*proto3.Value{stringProto("junk")},
- },
- &NullDate{civil.Date{}, true},
- errDecodeColumn(0, errBadEncoding(stringProto("junk"), func() error {
- _, err := civil.ParseDate("junk")
- return err
- }())),
- },
-
- {
- // Field specifies ARRAY<INT64> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(intType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]NullInt64{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<INT64> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(intType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullInt64{},
- errDecodeColumn(0, errNilListValue("INT64")),
- },
- {
- // Field specifies ARRAY<INT64> type, but value is for BYTES type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(intType()),
- },
- },
- []*proto3.Value{bytesProto([]byte("value"))},
- },
- &[]NullInt64{},
- errDecodeColumn(0, errSrcVal(bytesProto([]byte("value")), "List")),
- },
- {
- // Field specifies ARRAY<INT64> type, but value is for ARRAY<BOOL> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(intType()),
- },
- },
- []*proto3.Value{listProto(boolProto(true))},
- },
- &[]NullInt64{},
- errDecodeColumn(0, errDecodeArrayElement(0, boolProto(true),
- "INT64", errSrcVal(boolProto(true), "String"))),
- },
- {
- // Field specifies ARRAY<STRING> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(stringType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]NullString{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<STRING> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(stringType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullString{},
- errDecodeColumn(0, errNilListValue("STRING")),
- },
- {
- // Field specifies ARRAY<STRING> type, but value is for BOOL type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(stringType()),
- },
- },
- []*proto3.Value{boolProto(true)},
- },
- &[]NullString{},
- errDecodeColumn(0, errSrcVal(boolProto(true), "List")),
- },
- {
- // Field specifies ARRAY<STRING> type, but value is for ARRAY<BOOL> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(stringType()),
- },
- },
- []*proto3.Value{listProto(boolProto(true))},
- },
- &[]NullString{},
- errDecodeColumn(0, errDecodeArrayElement(0, boolProto(true),
- "STRING", errSrcVal(boolProto(true), "String"))),
- },
- {
- // Field specifies ARRAY<FLOAT64> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(floatType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]NullFloat64{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<FLOAT64> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(floatType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullFloat64{},
- errDecodeColumn(0, errNilListValue("FLOAT64")),
- },
- {
- // Field specifies ARRAY<FLOAT64> type, but value is for STRING type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(floatType()),
- },
- },
- []*proto3.Value{stringProto("value")},
- },
- &[]NullFloat64{},
- errDecodeColumn(0, errSrcVal(stringProto("value"), "List")),
- },
- {
- // Field specifies ARRAY<FLOAT64> type, but value is for ARRAY<BOOL> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(floatType()),
- },
- },
- []*proto3.Value{listProto(boolProto(true))},
- },
- &[]NullFloat64{},
- errDecodeColumn(0, errDecodeArrayElement(0, boolProto(true),
- "FLOAT64", errSrcVal(boolProto(true), "Number"))),
- },
- {
- // Field specifies ARRAY<BYTES> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(bytesType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[][]byte{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<BYTES> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(bytesType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[][]byte{},
- errDecodeColumn(0, errNilListValue("BYTES")),
- },
- {
- // Field specifies ARRAY<BYTES> type, but value is for FLOAT64 type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(bytesType()),
- },
- },
- []*proto3.Value{floatProto(1.0)},
- },
- &[][]byte{},
- errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")),
- },
- {
- // Field specifies ARRAY<BYTES> type, but value is for ARRAY<FLOAT64> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(bytesType()),
- },
- },
- []*proto3.Value{listProto(floatProto(1.0))},
- },
- &[][]byte{},
- errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0),
- "BYTES", errSrcVal(floatProto(1.0), "String"))),
- },
- {
- // Field specifies ARRAY<BOOL> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(boolType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]NullBool{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<BOOL> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(boolType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullBool{},
- errDecodeColumn(0, errNilListValue("BOOL")),
- },
- {
- // Field specifies ARRAY<BOOL> type, but value is for FLOAT64 type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(boolType()),
- },
- },
- []*proto3.Value{floatProto(1.0)},
- },
- &[]NullBool{},
- errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")),
- },
- {
- // Field specifies ARRAY<BOOL> type, but value is for ARRAY<FLOAT64> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(boolType()),
- },
- },
- []*proto3.Value{listProto(floatProto(1.0))},
- },
- &[]NullBool{},
- errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0),
- "BOOL", errSrcVal(floatProto(1.0), "Bool"))),
- },
- {
- // Field specifies ARRAY<TIMESTAMP> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(timeType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]NullTime{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<TIMESTAMP> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(timeType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullTime{},
- errDecodeColumn(0, errNilListValue("TIMESTAMP")),
- },
- {
- // Field specifies ARRAY<TIMESTAMP> type, but value is for FLOAT64 type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(timeType()),
- },
- },
- []*proto3.Value{floatProto(1.0)},
- },
- &[]NullTime{},
- errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")),
- },
- {
- // Field specifies ARRAY<TIMESTAMP> type, but value is for ARRAY<FLOAT64> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(timeType()),
- },
- },
- []*proto3.Value{listProto(floatProto(1.0))},
- },
- &[]NullTime{},
- errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0),
- "TIMESTAMP", errSrcVal(floatProto(1.0), "String"))),
- },
- {
- // Field specifies ARRAY<DATE> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(dateType()),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]NullDate{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<DATE> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(dateType()),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullDate{},
- errDecodeColumn(0, errNilListValue("DATE")),
- },
- {
- // Field specifies ARRAY<DATE> type, but value is for FLOAT64 type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(dateType()),
- },
- },
- []*proto3.Value{floatProto(1.0)},
- },
- &[]NullDate{},
- errDecodeColumn(0, errSrcVal(floatProto(1.0), "List")),
- },
- {
- // Field specifies ARRAY<DATE> type, but value is for ARRAY<FLOAT64> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(dateType()),
- },
- },
- []*proto3.Value{listProto(floatProto(1.0))},
- },
- &[]NullDate{},
- errDecodeColumn(0, errDecodeArrayElement(0, floatProto(1.0),
- "DATE", errSrcVal(floatProto(1.0), "String"))),
- },
- {
- // Field specifies ARRAY<STRUCT> type, value is having a nil Kind.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{{Kind: (*proto3.Value_ListValue)(nil)}},
- },
- &[]*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{},
- errDecodeColumn(0, errSrcVal(&proto3.Value{Kind: (*proto3.Value_ListValue)(nil)}, "List")),
- },
- {
- // Field specifies ARRAY<STRUCT> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{},
- errDecodeColumn(0, errNilListValue("STRUCT")),
- },
- {
- // Field specifies ARRAY<STRUCT> type, value is having a nil ListValue.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{{Kind: &proto3.Value_ListValue{}}},
- },
- &[]NullRow{},
- errDecodeColumn(0, errNilListValue("STRUCT")),
- },
- {
- // Field specifies ARRAY<STRUCT> type, value is for BYTES type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{bytesProto([]byte("value"))},
- },
- &[]*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{},
- errDecodeColumn(0, errSrcVal(bytesProto([]byte("value")), "List")),
- },
- {
- // Field specifies ARRAY<STRUCT> type, value is for BYTES type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{listProto(bytesProto([]byte("value")))},
- },
- &[]NullRow{},
- errDecodeColumn(0, errNotStructElement(0, bytesProto([]byte("value")))),
- },
- {
- // Field specifies ARRAY<STRUCT> type, value is for ARRAY<BYTES> type.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{listProto(bytesProto([]byte("value")))},
- },
- &[]*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{},
- errDecodeColumn(0, errDecodeArrayElement(0, bytesProto([]byte("value")),
- "STRUCT", errSrcVal(bytesProto([]byte("value")), "List"))),
- },
- {
- // Field specifies ARRAY<STRUCT>, but is having nil StructType.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- &sppb.Type{Code: sppb.TypeCode_STRUCT},
- ),
- },
- },
- []*proto3.Value{listProto(listProto(intProto(1), floatProto(2.0), stringProto("3")))},
- },
- &[]*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{},
- errDecodeColumn(0, errDecodeArrayElement(0, listProto(intProto(1), floatProto(2.0), stringProto("3")),
- "STRUCT", errNilSpannerStructType())),
- },
- {
- // Field specifies ARRAY<STRUCT>, but the second struct value is for BOOL type instead of FLOAT64.
- &Row{
- []*sppb.StructType_Field{
- {
- "Col0",
- listType(
- structType(
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- ),
- ),
- },
- },
- []*proto3.Value{listProto(listProto(intProto(1), boolProto(true), stringProto("3")))},
- },
- &[]*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{},
- errDecodeColumn(
- 0,
- errDecodeArrayElement(
- 0, listProto(intProto(1), boolProto(true), stringProto("3")), "STRUCT",
- errDecodeStructField(
- &sppb.StructType{
- Fields: []*sppb.StructType_Field{
- mkField("Col1", intType()),
- mkField("Col2", floatType()),
- mkField("Col3", stringType()),
- },
- },
- "Col2",
- errSrcVal(boolProto(true), "Number"),
- ),
- ),
- ),
- },
- } {
- if gotErr := test.row.Column(0, test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: test.row.Column(0) got error %v, want %v", i, gotErr, test.wantErr)
- }
- if gotErr := test.row.ColumnByName("Col0", test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: test.row.ColumnByName(%q) got error %v, want %v", i, "Col0", gotErr, test.wantErr)
- }
- if gotErr := test.row.Columns(test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
- t.Errorf("%v: test.row.Columns(%T) got error %v, want %v", i, test.dst, gotErr, test.wantErr)
- }
- }
-}
-
-// Test Row.ToStruct().
-func TestToStruct(t *testing.T) {
- s := []struct {
- // STRING / STRING ARRAY
- PrimaryKey string `spanner:"STRING"`
- NullString NullString `spanner:"NULL_STRING"`
- StringArray []NullString `spanner:"STRING_ARRAY"`
- NullStringArray []NullString `spanner:"NULL_STRING_ARRAY"`
- // BYTES / BYTES ARRAY
- Bytes []byte `spanner:"BYTES"`
- NullBytes []byte `spanner:"NULL_BYTES"`
- BytesArray [][]byte `spanner:"BYTES_ARRAY"`
- NullBytesArray [][]byte `spanner:"NULL_BYTES_ARRAY"`
- // INT64 / INT64 ARRAY
- Int64 int64 `spanner:"INT64"`
- NullInt64 NullInt64 `spanner:"NULL_INT64"`
- Int64Array []NullInt64 `spanner:"INT64_ARRAY"`
- NullInt64Array []NullInt64 `spanner:"NULL_INT64_ARRAY"`
- // BOOL / BOOL ARRAY
- Bool bool `spanner:"BOOL"`
- NullBool NullBool `spanner:"NULL_BOOL"`
- BoolArray []NullBool `spanner:"BOOL_ARRAY"`
- NullBoolArray []NullBool `spanner:"NULL_BOOL_ARRAY"`
- // FLOAT64 / FLOAT64 ARRAY
- Float64 float64 `spanner:"FLOAT64"`
- NullFloat64 NullFloat64 `spanner:"NULL_FLOAT64"`
- Float64Array []NullFloat64 `spanner:"FLOAT64_ARRAY"`
- NullFloat64Array []NullFloat64 `spanner:"NULL_FLOAT64_ARRAY"`
- // TIMESTAMP / TIMESTAMP ARRAY
- Timestamp time.Time `spanner:"TIMESTAMP"`
- NullTimestamp NullTime `spanner:"NULL_TIMESTAMP"`
- TimestampArray []NullTime `spanner:"TIMESTAMP_ARRAY"`
- NullTimestampArray []NullTime `spanner:"NULL_TIMESTAMP_ARRAY"`
- // DATE / DATE ARRAY
- Date civil.Date `spanner:"DATE"`
- NullDate NullDate `spanner:"NULL_DATE"`
- DateArray []NullDate `spanner:"DATE_ARRAY"`
- NullDateArray []NullDate `spanner:"NULL_DATE_ARRAY"`
-
- // STRUCT ARRAY
- StructArray []*struct {
- Col1 int64
- Col2 float64
- Col3 string
- } `spanner:"STRUCT_ARRAY"`
- NullStructArray []*struct {
- Col1 int64
- Col2 float64
- Col3 string
- } `spanner:"NULL_STRUCT_ARRAY"`
- }{
- {}, // got
- {
- // STRING / STRING ARRAY
- "value",
- NullString{},
- []NullString{{"value1", true}, {}, {"value3", true}},
- []NullString(nil),
- // BYTES / BYTES ARRAY
- []byte("value"),
- []byte(nil),
- [][]byte{[]byte("value1"), nil, []byte("value3")},
- [][]byte(nil),
- // INT64 / INT64 ARRAY
- int64(17),
- NullInt64{},
- []NullInt64{{int64(1), true}, {int64(2), true}, {}},
- []NullInt64(nil),
- // BOOL / BOOL ARRAY
- true,
- NullBool{},
- []NullBool{{}, {true, true}, {false, true}},
- []NullBool(nil),
- // FLOAT64 / FLOAT64 ARRAY
- 1.7,
- NullFloat64{},
- []NullFloat64{{}, {}, {1.7, true}},
- []NullFloat64(nil),
- // TIMESTAMP / TIMESTAMP ARRAY
- tm,
- NullTime{},
- []NullTime{{}, {tm, true}},
- []NullTime(nil),
- // DATE / DATE ARRAY
- dt,
- NullDate{},
- []NullDate{{}, {dt, true}},
- []NullDate(nil),
- // STRUCT ARRAY
- []*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{
- nil,
- &struct {
- Col1 int64
- Col2 float64
- Col3 string
- }{3, 33.3, "three"},
- nil,
- },
- []*struct {
- Col1 int64
- Col2 float64
- Col3 string
- }(nil),
- }, // want
- }
- err := row.ToStruct(&s[0])
- if err != nil {
- t.Errorf("row.ToStruct() returns error: %v, want nil", err)
- }
- if !reflect.DeepEqual(s[0], s[1]) {
- t.Errorf("row.ToStruct() fetches struct %v, want %v", s[0], s[1])
- }
-}
-
-// Test helpers for getting column names.
-func TestColumnNameAndIndex(t *testing.T) {
- // Test Row.Size().
- if rs := row.Size(); rs != len(row.fields) {
- t.Errorf("row.Size() returns %v, want %v", rs, len(row.fields))
- }
- // Test Row.Size() on empty Row.
- if rs := (&Row{}).Size(); rs != 0 {
- t.Errorf("empty_row.Size() returns %v, want %v", rs, 0)
- }
- // Test Row.ColumnName()
- for i, col := range row.fields {
- if cn := row.ColumnName(i); cn != col.Name {
- t.Errorf("row.ColumnName(%v) returns %q, want %q", i, cn, col.Name)
- }
- goti, err := row.ColumnIndex(col.Name)
- if err != nil {
- t.Errorf("ColumnIndex(%q) error %v", col.Name, err)
- continue
- }
- if goti != i {
- t.Errorf("ColumnIndex(%q) = %d, want %d", col.Name, goti, i)
- }
- }
- // Test Row.ColumnName on empty Row.
- if cn := (&Row{}).ColumnName(0); cn != "" {
- t.Errorf("empty_row.ColumnName(%v) returns %q, want %q", 0, cn, "")
- }
- // Test Row.ColumnIndex on empty Row.
- if _, err := (&Row{}).ColumnIndex(""); err == nil {
- t.Error("empty_row.ColumnIndex returns nil, want error")
- }
-}
-
-func TestNewRow(t *testing.T) {
- for _, test := range []struct {
- names []string
- values []interface{}
- want *Row
- wantErr error
- }{
- {
- want: &Row{fields: []*sppb.StructType_Field{}, vals: []*proto3.Value{}},
- },
- {
- names: []string{},
- values: []interface{}{},
- want: &Row{fields: []*sppb.StructType_Field{}, vals: []*proto3.Value{}},
- },
- {
- names: []string{"a", "b"},
- values: []interface{}{},
- want: nil,
- wantErr: errNamesValuesMismatch([]string{"a", "b"}, []interface{}{}),
- },
- {
- names: []string{"a", "b", "c"},
- values: []interface{}{5, "abc", GenericColumnValue{listType(intType()), listProto(intProto(91), nullProto(), intProto(87))}},
- want: &Row{
- []*sppb.StructType_Field{
- {"a", intType()},
- {"b", stringType()},
- {"c", listType(intType())},
- },
- []*proto3.Value{
- intProto(5),
- stringProto("abc"),
- listProto(intProto(91), nullProto(), intProto(87)),
- },
- },
- },
- } {
- got, err := NewRow(test.names, test.values)
- if !reflect.DeepEqual(err, test.wantErr) {
- t.Errorf("NewRow(%v,%v).err = %s, want %s", test.names, test.values, err, test.wantErr)
- continue
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("NewRow(%v,%v) = %s, want %s", test.names, test.values, got, test.want)
- continue
- }
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/session.go b/vendor/cloud.google.com/go/spanner/session.go
deleted file mode 100644
index 6930a3aba..000000000
--- a/vendor/cloud.google.com/go/spanner/session.go
+++ /dev/null
@@ -1,968 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "container/heap"
- "container/list"
- "fmt"
- "math/rand"
- "strings"
- "sync"
- "time"
-
- log "github.com/golang/glog"
- "golang.org/x/net/context"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-// sessionHandle is an interface for transactions to access Cloud Spanner sessions safely. It is generated by sessionPool.take().
-type sessionHandle struct {
- // mu guarantees that inner session object is returned / destroyed only once.
- mu sync.Mutex
- // session is a pointer to a session object. Transactions never need to access it directly.
- session *session
-}
-
-// recycle gives the inner session object back to its home session pool. It is safe to call recycle multiple times but only the first one would take effect.
-func (sh *sessionHandle) recycle() {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.session == nil {
- // sessionHandle has already been recycled.
- return
- }
- sh.session.recycle()
- sh.session = nil
-}
-
-// getID gets the Cloud Spanner session ID from the internal session object. getID returns empty string if the sessionHandle is nil or the inner session
-// object has been released by recycle / destroy.
-func (sh *sessionHandle) getID() string {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.session == nil {
- // sessionHandle has already been recycled/destroyed.
- return ""
- }
- return sh.session.getID()
-}
-
-// getClient gets the Cloud Spanner RPC client associated with the session ID in sessionHandle.
-func (sh *sessionHandle) getClient() sppb.SpannerClient {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.session == nil {
- return nil
- }
- return sh.session.client
-}
-
-// getMetadata returns the metadata associated with the session in sessionHandle.
-func (sh *sessionHandle) getMetadata() metadata.MD {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.session == nil {
- return nil
- }
- return sh.session.md
-}
-
-// getTransactionID returns the transaction id in the session if available.
-func (sh *sessionHandle) getTransactionID() transactionID {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if sh.session == nil {
- return nil
- }
- return sh.session.tx
-}
-
-// destroy destroys the inner session object. It is safe to call destroy multiple times and only the first call would attempt to
-// destroy the inner session object.
-func (sh *sessionHandle) destroy() {
- sh.mu.Lock()
- s := sh.session
- sh.session = nil
- sh.mu.Unlock()
- if s == nil {
- // sessionHandle has already been destroyed.
- return
- }
- s.destroy(false)
-}
-
-// session wraps a Cloud Spanner session ID through which transactions are created and executed.
-type session struct {
- // client is the RPC channel to Cloud Spanner. It is set only once during session's creation.
- client sppb.SpannerClient
- // id is the unique id of the session in Cloud Spanner. It is set only once during session's creation.
- id string
- // pool is the session's home session pool where it was created. It is set only once during session's creation.
- pool *sessionPool
- // createTime is the timestamp of the session's creation. It is set only once during session's creation.
- createTime time.Time
-
- // mu protects the following fields from concurrent access: both healthcheck workers and transactions can modify them.
- mu sync.Mutex
- // valid marks the validity of a session.
- valid bool
- // hcIndex is the index of the session inside the global healthcheck queue. If hcIndex < 0, session has been unregistered from the queue.
- hcIndex int
- // idleList is the linkedlist node which links the session to its home session pool's idle list. If idleList == nil, the
- // session is not in idle list.
- idleList *list.Element
- // nextCheck is the timestamp of next scheduled healthcheck of the session. It is maintained by the global health checker.
- nextCheck time.Time
- // checkingHelath is true if currently this session is being processed by health checker. Must be modified under health checker lock.
- checkingHealth bool
- // md is the Metadata to be sent with each request.
- md metadata.MD
- // tx contains the transaction id if the session has been prepared for write.
- tx transactionID
-}
-
-// isValid returns true if the session is still valid for use.
-func (s *session) isValid() bool {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.valid
-}
-
-// isWritePrepared returns true if the session is prepared for write.
-func (s *session) isWritePrepared() bool {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.tx != nil
-}
-
-// String implements fmt.Stringer for session.
-func (s *session) String() string {
- s.mu.Lock()
- defer s.mu.Unlock()
- return fmt.Sprintf("<id=%v, hcIdx=%v, idleList=%p, valid=%v, create=%v, nextcheck=%v>",
- s.id, s.hcIndex, s.idleList, s.valid, s.createTime, s.nextCheck)
-}
-
-// ping verifies if the session is still alive in Cloud Spanner.
-func (s *session) ping() error {
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
- return runRetryable(ctx, func(ctx context.Context) error {
- _, err := s.client.GetSession(contextWithOutgoingMetadata(ctx, s.pool.md), &sppb.GetSessionRequest{Name: s.getID()}) // s.getID is safe even when s is invalid.
- return err
- })
-}
-
-// refreshIdle refreshes the session's session ID if it is in its home session pool's idle list
-// and returns true if successful.
-func (s *session) refreshIdle() bool {
- s.mu.Lock()
- validAndIdle := s.valid && s.idleList != nil
- s.mu.Unlock()
- if !validAndIdle {
- // Optimization: return early if s is not valid or if s is not in idle list.
- return false
- }
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- var sid string
- err := runRetryable(ctx, func(ctx context.Context) error {
- session, e := s.client.CreateSession(contextWithOutgoingMetadata(ctx, s.pool.md), &sppb.CreateSessionRequest{Database: s.pool.db})
- if e != nil {
- return e
- }
- sid = session.Name
- return nil
- })
- if err != nil {
- return false
- }
- s.pool.mu.Lock()
- s.mu.Lock()
- var recycle bool
- if s.valid && s.idleList != nil {
- // session is in idle list, refresh its session id.
- sid, s.id = s.id, sid
- if s.tx != nil {
- s.tx = nil
- s.pool.idleWriteList.Remove(s.idleList)
- // We need to put this session back into the pool.
- recycle = true
- }
- }
- s.mu.Unlock()
- s.pool.mu.Unlock()
- if recycle {
- s.pool.recycle(s)
- }
- // If we fail to explicitly destroy the session, it will be eventually garbage collected by
- // Cloud Spanner.
- if err = runRetryable(ctx, func(ctx context.Context) error {
- _, e := s.client.DeleteSession(contextWithOutgoingMetadata(ctx, s.pool.md), &sppb.DeleteSessionRequest{Name: sid})
- return e
- }); err != nil && log.V(2) {
- log.Warningf("Failed to delete session %v. Error: %v", sid, err)
- }
- return true
-}
-
-// setHcIndex atomically sets the session's index in the healthcheck queue and returns the old index.
-func (s *session) setHcIndex(i int) int {
- s.mu.Lock()
- defer s.mu.Unlock()
- oi := s.hcIndex
- s.hcIndex = i
- return oi
-}
-
-// setIdleList atomically sets the session's idle list link and returns the old link.
-func (s *session) setIdleList(le *list.Element) *list.Element {
- s.mu.Lock()
- defer s.mu.Unlock()
- old := s.idleList
- s.idleList = le
- return old
-}
-
-// invalidate marks a session as invalid and returns the old validity.
-func (s *session) invalidate() bool {
- s.mu.Lock()
- defer s.mu.Unlock()
- ov := s.valid
- s.valid = false
- return ov
-}
-
-// setNextCheck sets the timestamp for next healthcheck on the session.
-func (s *session) setNextCheck(t time.Time) {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.nextCheck = t
-}
-
-// setTransactionID sets the transaction id in the session
-func (s *session) setTransactionID(tx transactionID) {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.tx = tx
-}
-
-// getID returns the session ID which uniquely identifies the session in Cloud Spanner.
-func (s *session) getID() string {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.id
-}
-
-// getHcIndex returns the session's index into the global healthcheck priority queue.
-func (s *session) getHcIndex() int {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.hcIndex
-}
-
-// getIdleList returns the session's link in its home session pool's idle list.
-func (s *session) getIdleList() *list.Element {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.idleList
-}
-
-// getNextCheck returns the timestamp for next healthcheck on the session.
-func (s *session) getNextCheck() time.Time {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.nextCheck
-}
-
-// recycle turns the session back to its home session pool.
-func (s *session) recycle() {
- s.setTransactionID(nil)
- if !s.pool.recycle(s) {
- // s is rejected by its home session pool because it expired and the session pool is currently having enough number of open sessions.
- s.destroy(false)
- }
-}
-
-// destroy removes the session from its home session pool, healthcheck queue and Cloud Spanner service.
-func (s *session) destroy(isExpire bool) bool {
- // Remove s from session pool.
- if !s.pool.remove(s, isExpire) {
- return false
- }
- // Unregister s from healthcheck queue.
- s.pool.hc.unregister(s)
- // Remove s from Cloud Spanner service.
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- // Ignore the error returned by runRetryable because even if we fail to explicitly destroy the session,
- // it will be eventually garbage collected by Cloud Spanner.
- err := runRetryable(ctx, func(ctx context.Context) error {
- _, e := s.client.DeleteSession(ctx, &sppb.DeleteSessionRequest{Name: s.getID()})
- return e
- })
- if err != nil && log.V(2) {
- log.Warningf("Failed to delete session %v. Error: %v", s.getID(), err)
- }
- return true
-}
-
-// prepareForWrite prepares the session for write if it is not already in that state.
-func (s *session) prepareForWrite(ctx context.Context) error {
- if s.isWritePrepared() {
- return nil
- }
- tx, err := beginTransaction(ctx, s.getID(), s.client)
- if err != nil {
- return err
- }
- s.setTransactionID(tx)
- return nil
-}
-
-// SessionPoolConfig stores configurations of a session pool.
-type SessionPoolConfig struct {
- // getRPCClient is the caller supplied method for getting a gRPC client to Cloud Spanner, this makes session pool able to use client pooling.
- getRPCClient func() (sppb.SpannerClient, error)
- // MaxOpened is the maximum number of opened sessions that is allowed by the
- // session pool. Default to NumChannels * 100.
- MaxOpened uint64
- // MinOpened is the minimum number of opened sessions that the session pool
- // tries to maintain. Session pool won't continue to expire sessions if number
- // of opened connections drops below MinOpened. However, if session is found
- // to be broken, it will still be evicted from session pool, therefore it is
- // posssible that the number of opened sessions drops below MinOpened.
- MinOpened uint64
- // MaxSessionAge is the maximum duration that a session can be reused, zero
- // means session pool will never expire sessions.
- MaxSessionAge time.Duration
- // MaxBurst is the maximum number of concurrent session creation requests. Defaults to 10.
- MaxBurst uint64
- // WriteSessions is the fraction of sessions we try to keep prepared for write.
- WriteSessions float64
- // HealthCheckWorkers is number of workers used by health checker for this pool.
- HealthCheckWorkers int
- // HealthCheckInterval is how often the health checker pings a session.
- HealthCheckInterval time.Duration
-}
-
-// errNoRPCGetter returns error for SessionPoolConfig missing getRPCClient method.
-func errNoRPCGetter() error {
- return spannerErrorf(codes.InvalidArgument, "require SessionPoolConfig.getRPCClient != nil, got nil")
-}
-
-// errMinOpenedGTMapOpened returns error for SessionPoolConfig.MaxOpened < SessionPoolConfig.MinOpened when SessionPoolConfig.MaxOpened is set.
-func errMinOpenedGTMaxOpened(spc *SessionPoolConfig) error {
- return spannerErrorf(codes.InvalidArgument,
- "require SessionPoolConfig.MaxOpened >= SessionPoolConfig.MinOpened, got %v and %v", spc.MaxOpened, spc.MinOpened)
-}
-
-// validate verifies that the SessionPoolConfig is good for use.
-func (spc *SessionPoolConfig) validate() error {
- if spc.getRPCClient == nil {
- return errNoRPCGetter()
- }
- if spc.MinOpened > spc.MaxOpened && spc.MaxOpened > 0 {
- return errMinOpenedGTMaxOpened(spc)
- }
- return nil
-}
-
-// sessionPool creates and caches Cloud Spanner sessions.
-type sessionPool struct {
- // mu protects sessionPool from concurrent access.
- mu sync.Mutex
- // valid marks the validity of the session pool.
- valid bool
- // db is the database name that all sessions in the pool are associated with.
- db string
- // idleList caches idle session IDs. Session IDs in this list can be allocated for use.
- idleList list.List
- // idleWriteList caches idle sessions which have been prepared for write.
- idleWriteList list.List
- // mayGetSession is for broadcasting that session retrival/creation may proceed.
- mayGetSession chan struct{}
- // numOpened is the total number of open sessions from the session pool.
- numOpened uint64
- // createReqs is the number of ongoing session creation requests.
- createReqs uint64
- // prepareReqs is the number of ongoing session preparation request.
- prepareReqs uint64
- // configuration of the session pool.
- SessionPoolConfig
- // Metadata to be sent with each request
- md metadata.MD
- // hc is the health checker
- hc *healthChecker
-}
-
-// newSessionPool creates a new session pool.
-func newSessionPool(db string, config SessionPoolConfig, md metadata.MD) (*sessionPool, error) {
- if err := config.validate(); err != nil {
- return nil, err
- }
- pool := &sessionPool{
- db: db,
- valid: true,
- mayGetSession: make(chan struct{}),
- SessionPoolConfig: config,
- md: md,
- }
- if config.HealthCheckWorkers == 0 {
- // With 10 workers and assuming average latency of 5 ms for BeginTransaction, we will be able to
- // prepare 2000 tx/sec in advance. If the rate of takeWriteSession is more than that, it will
- // degrade to doing BeginTransaction inline.
- // TODO: consider resizing the worker pool dynamically according to the load.
- config.HealthCheckWorkers = 10
- }
- if config.HealthCheckInterval == 0 {
- config.HealthCheckInterval = 5 * time.Minute
- }
- // On GCE VM, within the same region an healthcheck ping takes on average 10ms to finish, given a 5 minutes interval and
- // 10 healthcheck workers, a healthChecker can effectively mantain 100 checks_per_worker/sec * 10 workers * 300 seconds = 300K sessions.
- pool.hc = newHealthChecker(config.HealthCheckInterval, config.HealthCheckWorkers, pool)
- return pool, nil
-}
-
-// isValid checks if the session pool is still valid.
-func (p *sessionPool) isValid() bool {
- if p == nil {
- return false
- }
- p.mu.Lock()
- defer p.mu.Unlock()
- return p.valid
-}
-
-// close marks the session pool as closed.
-func (p *sessionPool) close() {
- if p == nil {
- return
- }
- p.mu.Lock()
- if !p.valid {
- p.mu.Unlock()
- return
- }
- p.valid = false
- p.mu.Unlock()
- p.hc.close()
- // destroy all the sessions
- p.hc.mu.Lock()
- allSessions := make([]*session, len(p.hc.queue.sessions))
- copy(allSessions, p.hc.queue.sessions)
- p.hc.mu.Unlock()
- for _, s := range allSessions {
- s.destroy(false)
- }
-}
-
-// errInvalidSessionPool returns error for using an invalid session pool.
-func errInvalidSessionPool() error {
- return spannerErrorf(codes.InvalidArgument, "invalid session pool")
-}
-
-// errGetSessionTimeout returns error for context timeout during sessionPool.take().
-func errGetSessionTimeout() error {
- return spannerErrorf(codes.Canceled, "timeout / context canceled during getting session")
-}
-
-// shouldPrepareWrite returns true if we should prepare more sessions for write.
-func (p *sessionPool) shouldPrepareWrite() bool {
- return float64(p.numOpened)*p.WriteSessions > float64(p.idleWriteList.Len()+int(p.prepareReqs))
-}
-
-func (p *sessionPool) createSession(ctx context.Context) (*session, error) {
- doneCreate := func(done bool) {
- p.mu.Lock()
- if !done {
- // Session creation failed, give budget back.
- p.numOpened--
- }
- p.createReqs--
- // Notify other waiters blocking on session creation.
- close(p.mayGetSession)
- p.mayGetSession = make(chan struct{})
- p.mu.Unlock()
- }
- sc, err := p.getRPCClient()
- if err != nil {
- doneCreate(false)
- return nil, err
- }
- var s *session
- err = runRetryable(ctx, func(ctx context.Context) error {
- sid, e := sc.CreateSession(ctx, &sppb.CreateSessionRequest{Database: p.db})
- if e != nil {
- return e
- }
- // If no error, construct the new session.
- s = &session{valid: true, client: sc, id: sid.Name, pool: p, createTime: time.Now(), md: p.md}
- p.hc.register(s)
- return nil
- })
- if err != nil {
- doneCreate(false)
- // Should return error directly because of the previous retries on CreateSession RPC.
- return nil, err
- }
- doneCreate(true)
- return s, nil
-}
-
-func (p *sessionPool) isHealthy(s *session) bool {
- if s.getNextCheck().Add(2 * p.hc.getInterval()).Before(time.Now()) {
- // TODO: figure out if we need to schedule a new healthcheck worker here.
- if err := s.ping(); shouldDropSession(err) {
- // The session is already bad, continue to fetch/create a new one.
- s.destroy(false)
- return false
- }
- p.hc.scheduledHC(s)
- }
- return true
-}
-
-// take returns a cached session if there are available ones; if there isn't any, it tries to allocate a new one.
-// Session returned by take should be used for read operations.
-func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
- ctx = contextWithOutgoingMetadata(ctx, p.md)
- for {
- var (
- s *session
- err error
- )
-
- p.mu.Lock()
- if !p.valid {
- p.mu.Unlock()
- return nil, errInvalidSessionPool()
- }
- if p.idleList.Len() > 0 {
- // Idle sessions are available, get one from the top of the idle list.
- s = p.idleList.Remove(p.idleList.Front()).(*session)
- } else if p.idleWriteList.Len() > 0 {
- s = p.idleWriteList.Remove(p.idleWriteList.Front()).(*session)
- }
- if s != nil {
- s.setIdleList(nil)
- p.mu.Unlock()
- // From here, session is no longer in idle list, so healthcheck workers won't destroy it.
- // If healthcheck workers failed to schedule healthcheck for the session timely, do the check here.
- // Because session check is still much cheaper than session creation, they should be reused as much as possible.
- if !p.isHealthy(s) {
- continue
- }
- return &sessionHandle{session: s}, nil
- }
- // Idle list is empty, block if session pool has reached max session creation concurrency or max number of open sessions.
- if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) {
- mayGetSession := p.mayGetSession
- p.mu.Unlock()
- select {
- case <-ctx.Done():
- return nil, errGetSessionTimeout()
- case <-mayGetSession:
- }
- continue
- }
- // Take budget before the actual session creation.
- p.numOpened++
- p.createReqs++
- p.mu.Unlock()
- if s, err = p.createSession(ctx); err != nil {
- return nil, toSpannerError(err)
- }
- return &sessionHandle{session: s}, nil
- }
-}
-
-// takeWriteSession returns a write prepared cached session if there are available ones; if there isn't any, it tries to allocate a new one.
-// Session returned should be used for read write transactions.
-func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, error) {
- ctx = contextWithOutgoingMetadata(ctx, p.md)
- for {
- var (
- s *session
- err error
- )
-
- p.mu.Lock()
- if !p.valid {
- p.mu.Unlock()
- return nil, errInvalidSessionPool()
- }
- if p.idleWriteList.Len() > 0 {
- // Idle sessions are available, get one from the top of the idle list.
- s = p.idleWriteList.Remove(p.idleWriteList.Front()).(*session)
- } else if p.idleList.Len() > 0 {
- s = p.idleList.Remove(p.idleList.Front()).(*session)
- }
- if s != nil {
- s.setIdleList(nil)
- p.mu.Unlock()
- // From here, session is no longer in idle list, so healthcheck workers won't destroy it.
- // If healthcheck workers failed to schedule healthcheck for the session timely, do the check here.
- // Because session check is still much cheaper than session creation, they should be reused as much as possible.
- if !p.isHealthy(s) {
- continue
- }
- if !s.isWritePrepared() {
- if err = s.prepareForWrite(ctx); err != nil {
- return nil, toSpannerError(err)
- }
- }
- return &sessionHandle{session: s}, nil
- }
- // Idle list is empty, block if session pool has reached max session creation concurrency or max number of open sessions.
- if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) {
- mayGetSession := p.mayGetSession
- p.mu.Unlock()
- select {
- case <-ctx.Done():
- return nil, errGetSessionTimeout()
- case <-mayGetSession:
- }
- continue
- }
-
- // Take budget before the actual session creation.
- p.numOpened++
- p.createReqs++
- p.mu.Unlock()
- if s, err = p.createSession(ctx); err != nil {
- return nil, toSpannerError(err)
- }
- if err = s.prepareForWrite(ctx); err != nil {
- return nil, toSpannerError(err)
- }
- return &sessionHandle{session: s}, nil
- }
-}
-
-// recycle puts session s back to the session pool's idle list, it returns true if the session pool successfully recycles session s.
-func (p *sessionPool) recycle(s *session) bool {
- p.mu.Lock()
- defer p.mu.Unlock()
- if !s.isValid() || !p.valid {
- // Reject the session if session is invalid or pool itself is invalid.
- return false
- }
- if p.MaxSessionAge != 0 && s.createTime.Add(p.MaxSessionAge).Before(time.Now()) && p.numOpened > p.MinOpened {
- // session expires and number of opened sessions exceeds MinOpened, let the session destroy itself.
- return false
- }
- // Hot sessions will be converging at the front of the list, cold sessions will be evicted by healthcheck workers.
- if s.isWritePrepared() {
- s.setIdleList(p.idleWriteList.PushFront(s))
- } else {
- s.setIdleList(p.idleList.PushFront(s))
- }
- // Broadcast that a session has been returned to idle list.
- close(p.mayGetSession)
- p.mayGetSession = make(chan struct{})
- return true
-}
-
-// remove atomically removes session s from the session pool and invalidates s.
-// If isExpire == true, the removal is triggered by session expiration and in such cases, only idle sessions can be removed.
-func (p *sessionPool) remove(s *session, isExpire bool) bool {
- p.mu.Lock()
- defer p.mu.Unlock()
- if isExpire && (p.numOpened <= p.MinOpened || s.getIdleList() == nil) {
- // Don't expire session if the session is not in idle list (in use), or if number of open sessions is going below p.MinOpened.
- return false
- }
- ol := s.setIdleList(nil)
- // If the session is in the idlelist, remove it.
- if ol != nil {
- // Remove from whichever list it is in.
- p.idleList.Remove(ol)
- p.idleWriteList.Remove(ol)
- }
- if s.invalidate() {
- // Decrease the number of opened sessions.
- p.numOpened--
- // Broadcast that a session has been destroyed.
- close(p.mayGetSession)
- p.mayGetSession = make(chan struct{})
- return true
- }
- return false
-}
-
-// hcHeap implements heap.Interface. It is used to create the priority queue for session healthchecks.
-type hcHeap struct {
- sessions []*session
-}
-
-// Len impelemnts heap.Interface.Len.
-func (h hcHeap) Len() int {
- return len(h.sessions)
-}
-
-// Less implements heap.Interface.Less.
-func (h hcHeap) Less(i, j int) bool {
- return h.sessions[i].getNextCheck().Before(h.sessions[j].getNextCheck())
-}
-
-// Swap implements heap.Interface.Swap.
-func (h hcHeap) Swap(i, j int) {
- h.sessions[i], h.sessions[j] = h.sessions[j], h.sessions[i]
- h.sessions[i].setHcIndex(i)
- h.sessions[j].setHcIndex(j)
-}
-
-// Push implements heap.Interface.Push.
-func (h *hcHeap) Push(s interface{}) {
- ns := s.(*session)
- ns.setHcIndex(len(h.sessions))
- h.sessions = append(h.sessions, ns)
-}
-
-// Pop implements heap.Interface.Pop.
-func (h *hcHeap) Pop() interface{} {
- old := h.sessions
- n := len(old)
- s := old[n-1]
- h.sessions = old[:n-1]
- s.setHcIndex(-1)
- return s
-}
-
-// healthChecker performs periodical healthchecks on registered sessions.
-type healthChecker struct {
- // mu protects concurrent access to hcQueue.
- mu sync.Mutex
- // queue is the priority queue for session healthchecks. Sessions with lower nextCheck rank higher in the queue.
- queue hcHeap
- // interval is the average interval between two healthchecks on a session.
- interval time.Duration
- // workers is the number of concurrent healthcheck workers.
- workers int
- // waitWorkers waits for all healthcheck workers to exit
- waitWorkers sync.WaitGroup
- // pool is the underlying session pool.
- pool *sessionPool
- // closed marks if a healthChecker has been closed.
- closed bool
-}
-
-// newHealthChecker initializes new instance of healthChecker.
-func newHealthChecker(interval time.Duration, workers int, pool *sessionPool) *healthChecker {
- if workers <= 0 {
- workers = 1
- }
- hc := &healthChecker{
- interval: interval,
- workers: workers,
- pool: pool,
- }
- for i := 0; i < hc.workers; i++ {
- hc.waitWorkers.Add(1)
- go hc.worker(i)
- }
- return hc
-}
-
-// close closes the healthChecker and waits for all healthcheck workers to exit.
-func (hc *healthChecker) close() {
- hc.mu.Lock()
- hc.closed = true
- hc.mu.Unlock()
- hc.waitWorkers.Wait()
-}
-
-// isClosing checks if a healthChecker is already closing.
-func (hc *healthChecker) isClosing() bool {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- return hc.closed
-}
-
-// getInterval gets the healthcheck interval.
-func (hc *healthChecker) getInterval() time.Duration {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- return hc.interval
-}
-
-// scheduledHCLocked schedules next healthcheck on session s with the assumption that hc.mu is being held.
-func (hc *healthChecker) scheduledHCLocked(s *session) {
- // The next healthcheck will be scheduled after [interval*0.5, interval*1.5) nanoseconds.
- nsFromNow := rand.Int63n(int64(hc.interval)) + int64(hc.interval)/2
- s.setNextCheck(time.Now().Add(time.Duration(nsFromNow)))
- if hi := s.getHcIndex(); hi != -1 {
- // Session is still being tracked by healthcheck workers.
- heap.Fix(&hc.queue, hi)
- }
-}
-
-// scheduledHC schedules next healthcheck on session s. It is safe to be called concurrently.
-func (hc *healthChecker) scheduledHC(s *session) {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- hc.scheduledHCLocked(s)
-}
-
-// register registers a session with healthChecker for periodical healthcheck.
-func (hc *healthChecker) register(s *session) {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- hc.scheduledHCLocked(s)
- heap.Push(&hc.queue, s)
-}
-
-// unregister unregisters a session from healthcheck queue.
-func (hc *healthChecker) unregister(s *session) {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- oi := s.setHcIndex(-1)
- if oi >= 0 {
- heap.Remove(&hc.queue, oi)
- }
-}
-
-// markDone marks that health check for session has been performed.
-func (hc *healthChecker) markDone(s *session) {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- s.checkingHealth = false
-}
-
-// healthCheck checks the health of the session and pings it if needed.
-func (hc *healthChecker) healthCheck(s *session) {
- defer hc.markDone(s)
- if !s.pool.isValid() {
- // Session pool is closed, perform a garbage collection.
- s.destroy(false)
- return
- }
- if s.pool.MaxSessionAge != 0 && s.createTime.Add(s.pool.MaxSessionAge).Before(time.Now()) {
- // Session reaches its maximum age, retire it. Failing that try to refresh it.
- if s.destroy(true) || !s.refreshIdle() {
- return
- }
- }
- if err := s.ping(); shouldDropSession(err) {
- // Ping failed, destroy the session.
- s.destroy(false)
- }
-}
-
-// worker performs the healthcheck on sessions in healthChecker's priority queue.
-func (hc *healthChecker) worker(i int) {
- if log.V(2) {
- log.Infof("Starting health check worker %v", i)
- }
- // Returns a session which we should ping to keep it alive.
- getNextForPing := func() *session {
- hc.pool.mu.Lock()
- defer hc.pool.mu.Unlock()
- hc.mu.Lock()
- defer hc.mu.Unlock()
- if hc.queue.Len() <= 0 {
- // Queue is empty.
- return nil
- }
- s := hc.queue.sessions[0]
- if s.getNextCheck().After(time.Now()) && hc.pool.valid {
- // All sessions have been checked recently.
- return nil
- }
- hc.scheduledHCLocked(s)
- if !s.checkingHealth {
- s.checkingHealth = true
- return s
- }
- return nil
- }
-
- // Returns a session which we should prepare for write.
- getNextForTx := func() *session {
- hc.pool.mu.Lock()
- defer hc.pool.mu.Unlock()
- if hc.pool.shouldPrepareWrite() {
- if hc.pool.idleList.Len() > 0 && hc.pool.valid {
- hc.mu.Lock()
- defer hc.mu.Unlock()
- if hc.pool.idleList.Front().Value.(*session).checkingHealth {
- return nil
- }
- session := hc.pool.idleList.Remove(hc.pool.idleList.Front()).(*session)
- session.checkingHealth = true
- hc.pool.prepareReqs++
- return session
- }
- }
- return nil
- }
-
- for {
- if hc.isClosing() {
- if log.V(2) {
- log.Infof("Closing health check worker %v", i)
- }
- // Exit when the pool has been closed and all sessions have been destroyed
- // or when health checker has been closed.
- hc.waitWorkers.Done()
- return
- }
- ws := getNextForTx()
- if ws != nil {
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
- ws.prepareForWrite(contextWithOutgoingMetadata(ctx, hc.pool.md))
- hc.pool.recycle(ws)
- hc.pool.mu.Lock()
- hc.pool.prepareReqs--
- hc.pool.mu.Unlock()
- hc.markDone(ws)
- }
- rs := getNextForPing()
- if rs == nil {
- if ws == nil {
- // No work to be done so sleep to avoid burning cpu
- pause := int64(100 * time.Millisecond)
- if pause > int64(hc.interval) {
- pause = int64(hc.interval)
- }
- <-time.After(time.Duration(rand.Int63n(pause) + pause/2))
- }
- continue
- }
- hc.healthCheck(rs)
- }
-}
-
-// shouldDropSession returns true if a particular error leads to the removal of a session
-func shouldDropSession(err error) bool {
- if err == nil {
- return false
- }
- // If a Cloud Spanner can no longer locate the session (for example, if session is garbage collected), then caller
- // should not try to return the session back into the session pool.
- // TODO: once gRPC can return auxilary error information, stop parsing the error message.
- if ErrCode(err) == codes.NotFound && strings.Contains(ErrDesc(err), "Session not found:") {
- return true
- }
- return false
-}
diff --git a/vendor/cloud.google.com/go/spanner/session_test.go b/vendor/cloud.google.com/go/spanner/session_test.go
deleted file mode 100644
index 7c3d4f88e..000000000
--- a/vendor/cloud.google.com/go/spanner/session_test.go
+++ /dev/null
@@ -1,792 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "container/heap"
- "math/rand"
- "reflect"
- "sync"
- "testing"
- "time"
-
- "golang.org/x/net/context"
-
- "cloud.google.com/go/spanner/internal/testutil"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
-)
-
-// setup prepares test environment for regular session pool tests.
-func setup(t *testing.T, spc SessionPoolConfig) (sp *sessionPool, sc *testutil.MockCloudSpannerClient, cancel func()) {
- sc = testutil.NewMockCloudSpannerClient(t)
- spc.getRPCClient = func() (sppb.SpannerClient, error) {
- return sc, nil
- }
- spc.HealthCheckInterval = 50 * time.Millisecond
- sp, err := newSessionPool("mockdb", spc, nil)
- if err != nil {
- t.Fatalf("cannot create session pool: %v", err)
- }
- cancel = func() {
- sp.close()
- }
- return
-}
-
-// TestSessionCreation tests session creation during sessionPool.Take().
-func TestSessionCreation(t *testing.T) {
- sp, sc, cancel := setup(t, SessionPoolConfig{})
- defer cancel()
- // Take three sessions from session pool, this should trigger session pool to create three new sessions.
- shs := make([]*sessionHandle, 3)
- // gotDs holds the unique sessions taken from session pool.
- gotDs := map[string]bool{}
- for i := 0; i < len(shs); i++ {
- var err error
- shs[i], err = sp.take(context.Background())
- if err != nil {
- t.Errorf("failed to get session(%v): %v", i, err)
- }
- gotDs[shs[i].getID()] = true
- }
- if len(gotDs) != len(shs) {
- t.Errorf("session pool created %v sessions, want %v", len(gotDs), len(shs))
- }
- if wantDs := sc.DumpSessions(); !reflect.DeepEqual(gotDs, wantDs) {
- t.Errorf("session pool creates sessions %v, want %v", gotDs, wantDs)
- }
- // Verify that created sessions are recorded correctly in session pool.
- sp.mu.Lock()
- if int(sp.numOpened) != len(shs) {
- t.Errorf("session pool reports %v open sessions, want %v", sp.numOpened, len(shs))
- }
- if sp.createReqs != 0 {
- t.Errorf("session pool reports %v session create requests, want 0", int(sp.createReqs))
- }
- sp.mu.Unlock()
- // Verify that created sessions are tracked correctly by healthcheck queue.
- hc := sp.hc
- hc.mu.Lock()
- if hc.queue.Len() != len(shs) {
- t.Errorf("healthcheck queue length = %v, want %v", hc.queue.Len(), len(shs))
- }
- for _, s := range hc.queue.sessions {
- if !gotDs[s.getID()] {
- t.Errorf("session %v is in healthcheck queue, but it is not created by session pool", s.getID())
- }
- }
- hc.mu.Unlock()
-}
-
-// TestTakeFromIdleList tests taking sessions from session pool's idle list.
-func TestTakeFromIdleList(t *testing.T) {
- sp, sc, cancel := setup(t, SessionPoolConfig{})
- defer cancel()
- // Take ten sessions from session pool and recycle them.
- shs := make([]*sessionHandle, 10)
- for i := 0; i < len(shs); i++ {
- var err error
- shs[i], err = sp.take(context.Background())
- if err != nil {
- t.Errorf("failed to get session(%v): %v", i, err)
- }
- }
- for i := 0; i < len(shs); i++ {
- shs[i].recycle()
- }
- // Further session requests from session pool won't cause mockclient to create more sessions.
- wantSessions := sc.DumpSessions()
- // Take ten sessions from session pool again, this time all sessions should come from idle list.
- gotSessions := map[string]bool{}
- for i := 0; i < len(shs); i++ {
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot take session from session pool: %v", err)
- }
- gotSessions[sh.getID()] = true
- }
- if len(gotSessions) != 10 {
- t.Errorf("got %v unique sessions, want 10", len(gotSessions))
- }
- if !reflect.DeepEqual(gotSessions, wantSessions) {
- t.Errorf("got sessions: %v, want %v", gotSessions, wantSessions)
- }
-}
-
-// TesttakeWriteSessionFromIdleList tests taking write sessions from session pool's idle list.
-func TestTakeWriteSessionFromIdleList(t *testing.T) {
- sp, sc, cancel := setup(t, SessionPoolConfig{})
- defer cancel()
- act := testutil.NewAction("Begin", nil)
- acts := make([]testutil.Action, 20)
- for i := 0; i < len(acts); i++ {
- acts[i] = act
- }
- sc.SetActions(acts...)
- // Take ten sessions from session pool and recycle them.
- shs := make([]*sessionHandle, 10)
- for i := 0; i < len(shs); i++ {
- var err error
- shs[i], err = sp.takeWriteSession(context.Background())
- if err != nil {
- t.Errorf("failed to get session(%v): %v", i, err)
- }
- }
- for i := 0; i < len(shs); i++ {
- shs[i].recycle()
- }
- // Further session requests from session pool won't cause mockclient to create more sessions.
- wantSessions := sc.DumpSessions()
- // Take ten sessions from session pool again, this time all sessions should come from idle list.
- gotSessions := map[string]bool{}
- for i := 0; i < len(shs); i++ {
- sh, err := sp.takeWriteSession(context.Background())
- if err != nil {
- t.Errorf("cannot take session from session pool: %v", err)
- }
- gotSessions[sh.getID()] = true
- }
- if len(gotSessions) != 10 {
- t.Errorf("got %v unique sessions, want 10", len(gotSessions))
- }
- if !reflect.DeepEqual(gotSessions, wantSessions) {
- t.Errorf("got sessions: %v, want %v", gotSessions, wantSessions)
- }
-}
-
-// TestTakeFromIdleListChecked tests taking sessions from session pool's idle list, but with a extra ping check.
-func TestTakeFromIdleListChecked(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{})
- defer cancel()
- // Stop healthcheck workers to simulate slow pings.
- sp.hc.close()
- // Create a session and recycle it.
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("failed to get session: %v", err)
- }
- wantSid := sh.getID()
- sh.recycle()
- <-time.After(time.Second)
- // Two back-to-back session requests, both of them should return the same session created before and
- // none of them should trigger a session ping.
- for i := 0; i < 2; i++ {
- // Take the session from the idle list and recycle it.
- sh, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("%v - failed to get session: %v", i, err)
- }
- if gotSid := sh.getID(); gotSid != wantSid {
- t.Errorf("%v - got session id: %v, want %v", i, gotSid, wantSid)
- }
- // The two back-to-back session requests shouldn't trigger any session pings because sessionPool.Take
- // reschedules the next healthcheck.
- if got, want := sc.DumpPings(), ([]string{wantSid}); !reflect.DeepEqual(got, want) {
- t.Errorf("%v - got ping session requests: %v, want %v", i, got, want)
- }
- sh.recycle()
- }
- // Inject session error to mockclient, and take the session from the session pool, the old session should be destroyed and
- // the session pool will create a new session.
- sc.InjectError("GetSession", grpc.Errorf(codes.NotFound, "Session not found:"))
- // Delay to trigger sessionPool.Take to ping the session.
- <-time.After(time.Second)
- sh, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("failed to get session: %v", err)
- }
- ds := sc.DumpSessions()
- if len(ds) != 1 {
- t.Errorf("dumped sessions from mockclient: %v, want %v", ds, sh.getID())
- }
- if sh.getID() == wantSid {
- t.Errorf("sessionPool.Take still returns the same session %v, want it to create a new one", wantSid)
- }
-}
-
-// TestTakeFromIdleWriteListChecked tests taking sessions from session pool's idle list, but with a extra ping check.
-func TestTakeFromIdleWriteListChecked(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{})
- defer cancel()
- sc.MakeNice()
- // Stop healthcheck workers to simulate slow pings.
- sp.hc.close()
- // Create a session and recycle it.
- sh, err := sp.takeWriteSession(context.Background())
- if err != nil {
- t.Errorf("failed to get session: %v", err)
- }
- wantSid := sh.getID()
- sh.recycle()
- <-time.After(time.Second)
- // Two back-to-back session requests, both of them should return the same session created before and
- // none of them should trigger a session ping.
- for i := 0; i < 2; i++ {
- // Take the session from the idle list and recycle it.
- sh, err = sp.takeWriteSession(context.Background())
- if err != nil {
- t.Errorf("%v - failed to get session: %v", i, err)
- }
- if gotSid := sh.getID(); gotSid != wantSid {
- t.Errorf("%v - got session id: %v, want %v", i, gotSid, wantSid)
- }
- // The two back-to-back session requests shouldn't trigger any session pings because sessionPool.Take
- // reschedules the next healthcheck.
- if got, want := sc.DumpPings(), ([]string{wantSid}); !reflect.DeepEqual(got, want) {
- t.Errorf("%v - got ping session requests: %v, want %v", i, got, want)
- }
- sh.recycle()
- }
- // Inject session error to mockclient, and take the session from the session pool, the old session should be destroyed and
- // the session pool will create a new session.
- sc.InjectError("GetSession", grpc.Errorf(codes.NotFound, "Session not found:"))
- // Delay to trigger sessionPool.Take to ping the session.
- <-time.After(time.Second)
- sh, err = sp.takeWriteSession(context.Background())
- if err != nil {
- t.Errorf("failed to get session: %v", err)
- }
- ds := sc.DumpSessions()
- if len(ds) != 1 {
- t.Errorf("dumped sessions from mockclient: %v, want %v", ds, sh.getID())
- }
- if sh.getID() == wantSid {
- t.Errorf("sessionPool.Take still returns the same session %v, want it to create a new one", wantSid)
- }
-}
-
-// TestMaxOpenedSessions tests max open sessions constraint.
-func TestMaxOpenedSessions(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, _, cancel := setup(t, SessionPoolConfig{MaxOpened: 1})
- defer cancel()
- sh1, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot take session from session pool: %v", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
- // Session request will timeout due to the max open sessions constraint.
- sh2, gotErr := sp.take(ctx)
- if wantErr := errGetSessionTimeout(); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("the second session retrival returns error %v, want %v", gotErr, wantErr)
- }
- go func() {
- <-time.After(time.Second)
- // destroy the first session to allow the next session request to proceed.
- sh1.destroy()
- }()
- // Now session request can be processed because the first session will be destroyed.
- sh2, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("after the first session is destroyed, session retrival still returns error %v, want nil", err)
- }
- if !sh2.session.isValid() || sh2.getID() == "" {
- t.Errorf("got invalid session: %v", sh2.session)
- }
-}
-
-// TestMinOpenedSessions tests min open session constraint.
-func TestMinOpenedSessions(t *testing.T) {
- sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1})
- defer cancel()
- // Take ten sessions from session pool and recycle them.
- var ss []*session
- var shs []*sessionHandle
- for i := 0; i < 10; i++ {
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("failed to get session(%v): %v", i, err)
- }
- ss = append(ss, sh.session)
- shs = append(shs, sh)
- sh.recycle()
- }
- for _, sh := range shs {
- sh.recycle()
- }
- // Simulate session expiration.
- for _, s := range ss {
- s.destroy(true)
- }
- sp.mu.Lock()
- defer sp.mu.Unlock()
- // There should be still one session left in idle list due to the min open sessions constraint.
- if sp.idleList.Len() != 1 {
- t.Errorf("got %v sessions in idle list, want 1", sp.idleList.Len())
- }
-}
-
-// TestMaxBurst tests max burst constraint.
-func TestMaxBurst(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{MaxBurst: 1})
- defer cancel()
- // Will cause session creation RPC to be retried forever.
- sc.InjectError("CreateSession", grpc.Errorf(codes.Unavailable, "try later"))
- // This session request will never finish until the injected error is cleared.
- go sp.take(context.Background())
- // Poll for the execution of the first session request.
- for {
- sp.mu.Lock()
- cr := sp.createReqs
- sp.mu.Unlock()
- if cr == 0 {
- <-time.After(time.Second)
- continue
- }
- // The first session request is being executed.
- break
- }
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
- defer cancel()
- sh, gotErr := sp.take(ctx)
- // Since MaxBurst == 1, the second session request should block.
- if wantErr := errGetSessionTimeout(); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("session retrival returns error %v, want %v", gotErr, wantErr)
- }
- // Let the first session request succeed.
- sc.InjectError("CreateSession", nil)
- // Now new session request can proceed because the first session request will eventually succeed.
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("session retrival returns error %v, want nil", err)
- }
- if !sh.session.isValid() || sh.getID() == "" {
- t.Errorf("got invalid session: %v", sh.session)
- }
-}
-
-// TestSessionrecycle tests recycling sessions.
-func TestSessionRecycle(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, _, cancel := setup(t, SessionPoolConfig{MaxSessionAge: 100 * time.Millisecond, MinOpened: 1})
- // Healthcheck is explicitly turned off in this test because it might aggressively expire sessions in idle list.
- sp.hc.close()
- defer cancel()
- var ss []*session
- shs := make([]*sessionHandle, 2)
- for i := 0; i < len(shs); i++ {
- var err error
- shs[i], err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get the session %v: %v", i, err)
- }
- ss = append(ss, shs[i].session)
- }
- // recycle the first session immediately.
- shs[0].recycle()
- // Let the second session expire.
- <-time.After(time.Second)
- // recycle the second session.
- shs[1].recycle()
- // Now the first session should be still valid, but the second session should have been destroyed.
- if !ss[0].isValid() {
- t.Errorf("the first session (%v) is invalid, want it to be valid", ss[0])
- }
- if ss[1].isValid() {
- t.Errorf("the second session (%v) is valid, want it to be invalid", ss[1])
- }
-}
-
-// TestSessionDestroy tests destroying sessions.
-func TestSessionDestroy(t *testing.T) {
- sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1})
- defer cancel()
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- s := sh.session
- sh.recycle()
- if d := s.destroy(true); d || !s.isValid() {
- // Session should be remaining because of min open sessions constraint.
- t.Errorf("session %v was destroyed in expiration mode, want it to stay alive", s)
- }
- if d := s.destroy(false); !d || s.isValid() {
- // Session should be destroyed.
- t.Errorf("failed to destroy session %s", s)
- }
-}
-
-// TestHcHeap tests heap operation on top of hcHeap.
-func TestHcHeap(t *testing.T) {
- in := []*session{
- &session{nextCheck: time.Unix(10, 0)},
- &session{nextCheck: time.Unix(0, 5)},
- &session{nextCheck: time.Unix(1, 8)},
- &session{nextCheck: time.Unix(11, 7)},
- &session{nextCheck: time.Unix(6, 3)},
- }
- want := []*session{
- &session{nextCheck: time.Unix(1, 8), hcIndex: 0},
- &session{nextCheck: time.Unix(6, 3), hcIndex: 1},
- &session{nextCheck: time.Unix(8, 2), hcIndex: 2},
- &session{nextCheck: time.Unix(10, 0), hcIndex: 3},
- &session{nextCheck: time.Unix(11, 7), hcIndex: 4},
- }
- hh := hcHeap{}
- for _, s := range in {
- heap.Push(&hh, s)
- }
- // Change top of the heap and do a adjustment.
- hh.sessions[0].nextCheck = time.Unix(8, 2)
- heap.Fix(&hh, 0)
- for idx := 0; hh.Len() > 0; idx++ {
- got := heap.Pop(&hh).(*session)
- want[idx].hcIndex = -1
- if !reflect.DeepEqual(got, want[idx]) {
- t.Errorf("%v: heap.Pop returns %v, want %v", idx, got, want[idx])
- }
- }
-}
-
-// TestHealthCheckScheduler tests if healthcheck workers can schedule and perform healthchecks properly.
-func TestHealthCheckScheduler(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{})
- defer cancel()
- // Create 50 sessions.
- ss := []string{}
- for i := 0; i < 50; i++ {
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- ss = append(ss, sh.getID())
- }
- // Sleep for 1s, allowing healthcheck workers to perform some session pings.
- <-time.After(time.Second)
- dp := sc.DumpPings()
- gotPings := map[string]int64{}
- for _, p := range dp {
- gotPings[p]++
- }
- for _, s := range ss {
- // The average ping interval is 50ms.
- want := int64(time.Second) / int64(50*time.Millisecond)
- if got := gotPings[s]; got < want/2 || got > want+want/2 {
- t.Errorf("got %v healthchecks on session %v, want it between (%v, %v)", got, s, want/2, want+want/2)
- }
- }
-}
-
-// Tests that a fractions of sessions are prepared for write by health checker.
-func TestWriteSessionsPrepared(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{WriteSessions: 0.5})
- sc.MakeNice()
- defer cancel()
- shs := make([]*sessionHandle, 10)
- var err error
- for i := 0; i < 10; i++ {
- shs[i], err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- }
- // Now there are 10 sessions in the pool. Release them.
- for _, sh := range shs {
- sh.recycle()
- }
- // Sleep for 1s, allowing healthcheck workers to invoke begin transaction.
- <-time.After(time.Second)
- wshs := make([]*sessionHandle, 5)
- for i := 0; i < 5; i++ {
- wshs[i], err = sp.takeWriteSession(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- if wshs[i].getTransactionID() == nil {
- t.Errorf("got nil transaction id from session pool")
- }
- }
- for _, sh := range wshs {
- sh.recycle()
- }
- <-time.After(time.Second)
- // Now force creation of 10 more sessions.
- shs = make([]*sessionHandle, 20)
- for i := 0; i < 20; i++ {
- shs[i], err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- }
- // Now there are 20 sessions in the pool. Release them.
- for _, sh := range shs {
- sh.recycle()
- }
- <-time.After(time.Second)
- if sp.idleWriteList.Len() != 10 {
- t.Errorf("Expect 10 write prepared session, got: %d", sp.idleWriteList.Len())
- }
-}
-
-// TestTakeFromWriteQueue tests that sessionPool.take() returns write prepared sessions as well.
-func TestTakeFromWriteQueue(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{MaxOpened: 1, WriteSessions: 1.0})
- sc.MakeNice()
- defer cancel()
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- sh.recycle()
- <-time.After(time.Second)
- // The session should now be in write queue but take should also return it.
- if sp.idleWriteList.Len() == 0 {
- t.Errorf("write queue unexpectedly empty")
- }
- if sp.idleList.Len() != 0 {
- t.Errorf("read queue not empty")
- }
- sh, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- sh.recycle()
-}
-
-// TestSessionHealthCheck tests healthchecking cases.
-func TestSessionHealthCheck(t *testing.T) {
- if testing.Short() {
- t.SkipNow()
- }
- sp, sc, cancel := setup(t, SessionPoolConfig{MaxSessionAge: 2 * time.Second})
- defer cancel()
- // Test pinging sessions.
- sh, err := sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- <-time.After(time.Second)
- pings := sc.DumpPings()
- if len(pings) == 0 || pings[0] != sh.getID() {
- t.Errorf("healthchecker didn't send any ping to session %v", sh.getID())
- }
- // Test expiring sessions.
- s := sh.session
- sh.recycle()
- // Sleep enough long for session in idle list to expire.
- <-time.After(2 * time.Second)
- if s.isValid() {
- t.Errorf("session(%v) is still alive, want it to expire", s)
- }
- // Test broken session detection.
- sh, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- sc.InjectError("GetSession", grpc.Errorf(codes.NotFound, "Session not found:"))
- // Wait for healthcheck workers to find the broken session and tear it down.
- <-time.After(1 * time.Second)
- if sh.session.isValid() {
- t.Errorf("session(%v) is still alive, want it to be dropped by healthcheck workers", s)
- }
- sc.InjectError("GetSession", nil)
- // Test garbage collection.
- sh, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- sp.close()
- if sh.session.isValid() {
- t.Errorf("session(%v) is still alive, want it to be garbage collected", s)
- }
- // Test session id refresh.
- // Recreate the session pool with min open sessions constraint.
- sp, err = newSessionPool("mockdb", SessionPoolConfig{
- MaxSessionAge: time.Second,
- MinOpened: 1,
- getRPCClient: func() (sppb.SpannerClient, error) {
- return sc, nil
- },
- HealthCheckInterval: 50 * time.Millisecond,
- }, nil)
- sh, err = sp.take(context.Background())
- if err != nil {
- t.Errorf("cannot get session from session pool: %v", err)
- }
- oid := sh.getID()
- s = sh.session
- sh.recycle()
- <-time.After(2 * time.Second)
- nid := s.getID()
- if nid == "" || nid == oid {
- t.Errorf("healthcheck workers failed to refresh session: oid=%v, nid=%v", oid, nid)
- }
- if gotDs, wantDs := sc.DumpSessions(), (map[string]bool{nid: true}); !reflect.DeepEqual(gotDs, wantDs) {
- t.Errorf("sessions in mockclient: %v, want %v", gotDs, wantDs)
- }
-}
-
-// TestStressSessionPool does stress test on session pool by the following concurrent operations:
-// 1) Test worker gets a session from the pool.
-// 2) Test worker turns a session back into the pool.
-// 3) Test worker destroys a session got from the pool.
-// 4) Healthcheck retires an old session from the pool's idlelist by refreshing its session id.
-// 5) Healthcheck destroys a broken session (because a worker has already destroyed it).
-// 6) Test worker closes the session pool.
-//
-// During the test, it is expected that all sessions that are taken from session pool remains valid and
-// when all test workers and healthcheck workers exit, mockclient, session pool and healthchecker should be in consistent state.
-func TestStressSessionPool(t *testing.T) {
- // Use concurrent workers to test different session pool built from different configurations.
- if testing.Short() {
- t.SkipNow()
- }
- for ti, cfg := range []SessionPoolConfig{
- SessionPoolConfig{},
- SessionPoolConfig{MaxSessionAge: 20 * time.Millisecond},
- SessionPoolConfig{MinOpened: 10, MaxOpened: 100},
- SessionPoolConfig{MaxBurst: 50},
- SessionPoolConfig{MaxSessionAge: 20 * time.Millisecond, MinOpened: 10, MaxOpened: 200, MaxBurst: 5},
- SessionPoolConfig{MaxSessionAge: 20 * time.Millisecond, MinOpened: 10, MaxOpened: 200, MaxBurst: 5, WriteSessions: 0.2},
- } {
- var wg sync.WaitGroup
- // Create a more aggressive session healthchecker to increase test concurrency.
- cfg.HealthCheckInterval = 50 * time.Millisecond
- cfg.HealthCheckWorkers = 50
- sc := testutil.NewMockCloudSpannerClient(t)
- sc.MakeNice()
- cfg.getRPCClient = func() (sppb.SpannerClient, error) {
- return sc, nil
- }
- sp, _ := newSessionPool("mockdb", cfg, nil)
- for i := 0; i < 100; i++ {
- wg.Add(1)
- // Schedule a test worker.
- go func(idx int, pool *sessionPool, client sppb.SpannerClient) {
- defer wg.Done()
- // Test worker iterates 1K times and tries different session / session pool operations.
- for j := 0; j < 1000; j++ {
- if idx%10 == 0 && j >= 900 {
- // Close the pool in selected set of workers during the middle of the test.
- pool.close()
- }
- // Take a write sessions ~ 20% of the times.
- takeWrite := rand.Intn(5) == 4
- var (
- sh *sessionHandle
- gotErr error
- )
- if takeWrite {
- sh, gotErr = pool.takeWriteSession(context.Background())
- } else {
- sh, gotErr = pool.take(context.Background())
- }
- if gotErr != nil {
- if pool.isValid() {
- t.Errorf("%v.%v: pool.take returns error when pool is still valid: %v", ti, idx, gotErr)
- }
- if wantErr := errInvalidSessionPool(); !reflect.DeepEqual(gotErr, wantErr) {
- t.Errorf("%v.%v: got error when pool is closed: %v, want %v", ti, idx, gotErr, wantErr)
- }
- continue
- }
- // Verify if session is valid when session pool is valid. Note that if session pool is invalid after sh is taken,
- // then sh might be invalidated by healthcheck workers.
- if (sh.getID() == "" || sh.session == nil || !sh.session.isValid()) && pool.isValid() {
- t.Errorf("%v.%v.%v: pool.take returns invalid session %v", ti, idx, takeWrite, sh.session)
- }
- if takeWrite && sh.getTransactionID() == nil {
- t.Errorf("%v.%v: pool.takeWriteSession returns session %v without transaction", ti, idx, sh.session)
- }
- if int64(cfg.MaxSessionAge) > 0 && rand.Intn(100) < idx {
- // Random sleep before destroying/recycling the session, to give healthcheck worker a chance to step in.
- <-time.After(time.Duration(rand.Int63n(int64(cfg.MaxSessionAge))))
- }
- if rand.Intn(100) < idx {
- // destroy the session.
- sh.destroy()
- continue
- }
- // recycle the session.
- sh.recycle()
- }
- }(i, sp, sc)
- }
- wg.Wait()
- sp.hc.close()
- // Here the states of healthchecker, session pool and mockclient are stable.
- idleSessions := map[string]bool{}
- hcSessions := map[string]bool{}
- mockSessions := sc.DumpSessions()
- // Dump session pool's idle list.
- for sl := sp.idleList.Front(); sl != nil; sl = sl.Next() {
- s := sl.Value.(*session)
- if idleSessions[s.getID()] {
- t.Errorf("%v: found duplicated session in idle list: %v", ti, s.getID())
- }
- idleSessions[s.getID()] = true
- }
- for sl := sp.idleWriteList.Front(); sl != nil; sl = sl.Next() {
- s := sl.Value.(*session)
- if idleSessions[s.getID()] {
- t.Errorf("%v: found duplicated session in idle write list: %v", ti, s.getID())
- }
- idleSessions[s.getID()] = true
- }
- if int(sp.numOpened) != len(idleSessions) {
- t.Errorf("%v: number of opened sessions (%v) != number of idle sessions (%v)", ti, sp.numOpened, len(idleSessions))
- }
- if sp.createReqs != 0 {
- t.Errorf("%v: number of pending session creations = %v, want 0", ti, sp.createReqs)
- }
- // Dump healthcheck queue.
- for _, s := range sp.hc.queue.sessions {
- if hcSessions[s.getID()] {
- t.Errorf("%v: found duplicated session in healthcheck queue: %v", ti, s.getID())
- }
- hcSessions[s.getID()] = true
- }
- // Verify that idleSessions == hcSessions == mockSessions.
- if !reflect.DeepEqual(idleSessions, hcSessions) {
- t.Errorf("%v: sessions in idle list (%v) != sessions in healthcheck queue (%v)", ti, idleSessions, hcSessions)
- }
- if !reflect.DeepEqual(hcSessions, mockSessions) {
- t.Errorf("%v: sessions in healthcheck queue (%v) != sessions in mockclient (%v)", ti, hcSessions, mockSessions)
- }
- sp.close()
- mockSessions = sc.DumpSessions()
- if len(mockSessions) != 0 {
- t.Errorf("Found live sessions: %v", mockSessions)
- }
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/spanner_test.go b/vendor/cloud.google.com/go/spanner/spanner_test.go
deleted file mode 100644
index ede4e80d2..000000000
--- a/vendor/cloud.google.com/go/spanner/spanner_test.go
+++ /dev/null
@@ -1,1234 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
- "math"
- "reflect"
- "strings"
- "sync"
- "testing"
- "time"
-
- "cloud.google.com/go/civil"
- "cloud.google.com/go/internal/testutil"
- database "cloud.google.com/go/spanner/admin/database/apiv1"
- "golang.org/x/net/context"
- "google.golang.org/api/iterator"
- "google.golang.org/api/option"
- "google.golang.org/grpc/codes"
-
- adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
-)
-
-var (
- // testProjectID specifies the project used for testing.
- // It can be changed by setting environment variable GCLOUD_TESTS_GOLANG_PROJECT_ID.
- testProjectID = testutil.ProjID()
- // testInstanceID specifies the Cloud Spanner instance used for testing.
- testInstanceID = "go-integration-test"
-
- // client is a spanner.Client.
- client *Client
- // admin is a spanner.DatabaseAdminClient.
- admin *database.DatabaseAdminClient
- // db is the path of the testing database.
- db string
- // dbName is the short name of the testing database.
- dbName string
-)
-
-var (
- singerDBStatements = []string{
- `CREATE TABLE Singers (
- SingerId INT64 NOT NULL,
- FirstName STRING(1024),
- LastName STRING(1024),
- SingerInfo BYTES(MAX)
- ) PRIMARY KEY (SingerId)`,
- `CREATE INDEX SingerByName ON Singers(FirstName, LastName)`,
- `CREATE TABLE Accounts (
- AccountId INT64 NOT NULL,
- Nickname STRING(100),
- Balance INT64 NOT NULL,
- ) PRIMARY KEY (AccountId)`,
- `CREATE INDEX AccountByNickname ON Accounts(Nickname) STORING (Balance)`,
- `CREATE TABLE Types (
- RowID INT64 NOT NULL,
- String STRING(MAX),
- StringArray ARRAY<STRING(MAX)>,
- Bytes BYTES(MAX),
- BytesArray ARRAY<BYTES(MAX)>,
- Int64a INT64,
- Int64Array ARRAY<INT64>,
- Bool BOOL,
- BoolArray ARRAY<BOOL>,
- Float64 FLOAT64,
- Float64Array ARRAY<FLOAT64>,
- Date DATE,
- DateArray ARRAY<DATE>,
- Timestamp TIMESTAMP,
- TimestampArray ARRAY<TIMESTAMP>,
- ) PRIMARY KEY (RowID)`,
- }
-
- readDBStatements = []string{
- `CREATE TABLE TestTable (
- Key STRING(MAX) NOT NULL,
- StringValue STRING(MAX)
- ) PRIMARY KEY (Key)`,
- `CREATE INDEX TestTableByValue ON TestTable(StringValue)`,
- `CREATE INDEX TestTableByValueDesc ON TestTable(StringValue DESC)`,
- }
-)
-
-type testTableRow struct{ Key, StringValue string }
-
-// prepare initializes Cloud Spanner testing DB and clients.
-func prepare(ctx context.Context, t *testing.T, statements []string) error {
- if testing.Short() {
- t.Skip("Integration tests skipped in short mode")
- }
- if testProjectID == "" {
- t.Skip("Integration tests skipped: GCLOUD_TESTS_GOLANG_PROJECT_ID is missing")
- }
- ts := testutil.TokenSource(ctx, AdminScope, Scope)
- if ts == nil {
- t.Skip("Integration test skipped: cannot get service account credential from environment variable %v", "GCLOUD_TESTS_GOLANG_KEY")
- }
- var err error
- // Create Admin client and Data client.
- // TODO: Remove the EndPoint option once this is the default.
- admin, err = database.NewDatabaseAdminClient(ctx, option.WithTokenSource(ts), option.WithEndpoint("spanner.googleapis.com:443"))
- if err != nil {
- t.Errorf("cannot create admin client: %v", err)
- return err
- }
- // Construct test DB name.
- dbName = fmt.Sprintf("gotest_%v", time.Now().UnixNano())
- db = fmt.Sprintf("projects/%v/instances/%v/databases/%v", testProjectID, testInstanceID, dbName)
- // Create database and tables.
- op, err := admin.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
- Parent: fmt.Sprintf("projects/%v/instances/%v", testProjectID, testInstanceID),
- CreateStatement: "CREATE DATABASE " + dbName,
- ExtraStatements: statements,
- })
- if err != nil {
- t.Errorf("cannot create testing DB %v: %v", db, err)
- return err
- }
- if _, err := op.Wait(ctx); err != nil {
- t.Errorf("cannot create testing DB %v: %v", db, err)
- return err
- }
- client, err = NewClientWithConfig(ctx, db, ClientConfig{
- SessionPoolConfig: SessionPoolConfig{
- WriteSessions: 0.2,
- },
- }, option.WithTokenSource(ts))
- if err != nil {
- t.Errorf("cannot create data client on DB %v: %v", db, err)
- return err
- }
- return nil
-}
-
-// tearDown tears down the testing environment created by prepare().
-func tearDown(ctx context.Context, t *testing.T) {
- if admin != nil {
- if err := admin.DropDatabase(ctx, &adminpb.DropDatabaseRequest{db}); err != nil {
- t.Logf("failed to drop testing database: %v, might need a manual removal", db)
- }
- admin.Close()
- }
- if client != nil {
- client.Close()
- }
- admin = nil
- client = nil
- db = ""
-}
-
-// Test SingleUse transaction.
-func TestSingleUse(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
- // Set up testing environment.
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- // If prepare() fails, tear down whatever that's already up.
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- // After all tests, tear down testing environment.
- defer tearDown(ctx, t)
-
- writes := []struct {
- row []interface{}
- ts time.Time
- }{
- {row: []interface{}{1, "Marc", "Foo"}},
- {row: []interface{}{2, "Tars", "Bar"}},
- {row: []interface{}{3, "Alpha", "Beta"}},
- {row: []interface{}{4, "Last", "End"}},
- }
- // Try to write four rows through the Apply API.
- for i, w := range writes {
- var err error
- m := InsertOrUpdate("Singers",
- []string{"SingerId", "FirstName", "LastName"},
- w.row)
- if writes[i].ts, err = client.Apply(ctx, []*Mutation{m}, ApplyAtLeastOnce()); err != nil {
- t.Fatal(err)
- }
- }
-
- // For testing timestamp bound staleness.
- <-time.After(time.Second)
-
- // Test reading rows with different timestamp bounds.
- for i, test := range []struct {
- want [][]interface{}
- tb TimestampBound
- checkTs func(time.Time) error
- }{
- {
- // strong
- [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}},
- StrongRead(),
- func(ts time.Time) error {
- // writes[3] is the last write, all subsequent strong read should have a timestamp larger than that.
- if ts.Before(writes[3].ts) {
- return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts)
- }
- return nil
- },
- },
- {
- // min_read_timestamp
- [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}},
- MinReadTimestamp(writes[3].ts),
- func(ts time.Time) error {
- if ts.Before(writes[3].ts) {
- return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts)
- }
- return nil
- },
- },
- {
- // max_staleness
- [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}},
- MaxStaleness(time.Second),
- func(ts time.Time) error {
- if ts.Before(writes[3].ts) {
- return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts)
- }
- return nil
- },
- },
- {
- // read_timestamp
- [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}},
- ReadTimestamp(writes[2].ts),
- func(ts time.Time) error {
- if ts != writes[2].ts {
- return fmt.Errorf("read got timestamp %v, expect %v", ts, writes[2].ts)
- }
- return nil
- },
- },
- {
- // exact_staleness
- nil,
- // Specify a staleness which should be already before this test because
- // context timeout is set to be 10s.
- ExactStaleness(11 * time.Second),
- func(ts time.Time) error {
- if ts.After(writes[0].ts) {
- return fmt.Errorf("read got timestamp %v, want it to be no earlier than %v", ts, writes[0].ts)
- }
- return nil
- },
- },
- } {
- // SingleUse.Query
- su := client.Single().WithTimestampBound(test.tb)
- got, err := readAll(su.Query(
- ctx,
- Statement{
- "SELECT SingerId, FirstName, LastName FROM Singers WHERE SingerId IN (@id1, @id3, @id4)",
- map[string]interface{}{"id1": int64(1), "id3": int64(3), "id4": int64(4)},
- }))
- if err != nil {
- t.Errorf("%d: SingleUse.Query returns error %v, want nil", i, err)
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: got unexpected result from SingleUse.Query: %v, want %v", i, got, test.want)
- }
- rts, err := su.Timestamp()
- if err != nil {
- t.Errorf("%d: SingleUse.Query doesn't return a timestamp, error: %v", i, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: SingleUse.Query doesn't return expected timestamp: %v", i, err)
- }
- // SingleUse.Read
- su = client.Single().WithTimestampBound(test.tb)
- got, err = readAll(su.Read(ctx, "Singers", KeySets(Key{1}, Key{3}, Key{4}), []string{"SingerId", "FirstName", "LastName"}))
- if err != nil {
- t.Errorf("%d: SingleUse.Read returns error %v, want nil", i, err)
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: got unexpected result from SingleUse.Read: %v, want %v", i, got, test.want)
- }
- rts, err = su.Timestamp()
- if err != nil {
- t.Errorf("%d: SingleUse.Read doesn't return a timestamp, error: %v", i, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: SingleUse.Read doesn't return expected timestamp: %v", i, err)
- }
- // SingleUse.ReadRow
- got = nil
- for _, k := range []Key{Key{1}, Key{3}, Key{4}} {
- su = client.Single().WithTimestampBound(test.tb)
- r, err := su.ReadRow(ctx, "Singers", k, []string{"SingerId", "FirstName", "LastName"})
- if err != nil {
- continue
- }
- v, err := rowToValues(r)
- if err != nil {
- continue
- }
- got = append(got, v)
- rts, err = su.Timestamp()
- if err != nil {
- t.Errorf("%d: SingleUse.ReadRow(%v) doesn't return a timestamp, error: %v", i, k, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: SingleUse.ReadRow(%v) doesn't return expected timestamp: %v", i, k, err)
- }
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: got unexpected results from SingleUse.ReadRow: %v, want %v", i, got, test.want)
- }
- // SingleUse.ReadUsingIndex
- su = client.Single().WithTimestampBound(test.tb)
- got, err = readAll(su.ReadUsingIndex(ctx, "Singers", "SingerByName", KeySets(Key{"Marc", "Foo"}, Key{"Alpha", "Beta"}, Key{"Last", "End"}), []string{"SingerId", "FirstName", "LastName"}))
- if err != nil {
- t.Errorf("%d: SingleUse.ReadUsingIndex returns error %v, want nil", i, err)
- }
- // The results from ReadUsingIndex is sorted by the index rather than primary key.
- if len(got) != len(test.want) {
- t.Errorf("%d: got unexpected result from SingleUse.ReadUsingIndex: %v, want %v", i, got, test.want)
- }
- for j, g := range got {
- if j > 0 {
- prev := got[j-1][1].(string) + got[j-1][2].(string)
- curr := got[j][1].(string) + got[j][2].(string)
- if strings.Compare(prev, curr) > 0 {
- t.Errorf("%d: SingleUse.ReadUsingIndex fails to order rows by index keys, %v should be after %v", i, got[j-1], got[j])
- }
- }
- found := false
- for _, w := range test.want {
- if reflect.DeepEqual(g, w) {
- found = true
- }
- }
- if !found {
- t.Errorf("%d: got unexpected result from SingleUse.ReadUsingIndex: %v, want %v", i, got, test.want)
- break
- }
- }
- rts, err = su.Timestamp()
- if err != nil {
- t.Errorf("%d: SingleUse.ReadUsingIndex doesn't return a timestamp, error: %v", i, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: SingleUse.ReadUsingIndex doesn't return expected timestamp: %v", i, err)
- }
- }
-}
-
-// Test ReadOnlyTransaction. The testsuite is mostly like SingleUse, except it
-// also tests for a single timestamp across multiple reads.
-func TestReadOnlyTransaction(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
- // Set up testing environment.
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- // If prepare() fails, tear down whatever that's already up.
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- // After all tests, tear down testing environment.
- defer tearDown(ctx, t)
-
- writes := []struct {
- row []interface{}
- ts time.Time
- }{
- {row: []interface{}{1, "Marc", "Foo"}},
- {row: []interface{}{2, "Tars", "Bar"}},
- {row: []interface{}{3, "Alpha", "Beta"}},
- {row: []interface{}{4, "Last", "End"}},
- }
- // Try to write four rows through the Apply API.
- for i, w := range writes {
- var err error
- m := InsertOrUpdate("Singers",
- []string{"SingerId", "FirstName", "LastName"},
- w.row)
- if writes[i].ts, err = client.Apply(ctx, []*Mutation{m}, ApplyAtLeastOnce()); err != nil {
- t.Fatal(err)
- }
- }
-
- // For testing timestamp bound staleness.
- <-time.After(time.Second)
-
- // Test reading rows with different timestamp bounds.
- for i, test := range []struct {
- want [][]interface{}
- tb TimestampBound
- checkTs func(time.Time) error
- }{
- // Note: min_read_timestamp and max_staleness are not supported by ReadOnlyTransaction. See
- // API document for more details.
- {
- // strong
- [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}, {int64(4), "Last", "End"}},
- StrongRead(),
- func(ts time.Time) error {
- if ts.Before(writes[3].ts) {
- return fmt.Errorf("read got timestamp %v, want it to be no later than %v", ts, writes[3].ts)
- }
- return nil
- },
- },
- {
- // read_timestamp
- [][]interface{}{{int64(1), "Marc", "Foo"}, {int64(3), "Alpha", "Beta"}},
- ReadTimestamp(writes[2].ts),
- func(ts time.Time) error {
- if ts != writes[2].ts {
- return fmt.Errorf("read got timestamp %v, expect %v", ts, writes[2].ts)
- }
- return nil
- },
- },
- {
- // exact_staleness
- nil,
- // Specify a staleness which should be already before this test because
- // context timeout is set to be 10s.
- ExactStaleness(11 * time.Second),
- func(ts time.Time) error {
- if ts.After(writes[0].ts) {
- return fmt.Errorf("read got timestamp %v, want it to be no earlier than %v", ts, writes[0].ts)
- }
- return nil
- },
- },
- } {
- // ReadOnlyTransaction.Query
- ro := client.ReadOnlyTransaction().WithTimestampBound(test.tb)
- got, err := readAll(ro.Query(
- ctx,
- Statement{
- "SELECT SingerId, FirstName, LastName FROM Singers WHERE SingerId IN (@id1, @id3, @id4)",
- map[string]interface{}{"id1": int64(1), "id3": int64(3), "id4": int64(4)},
- }))
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.Query returns error %v, want nil", i, err)
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: got unexpected result from ReadOnlyTransaction.Query: %v, want %v", i, got, test.want)
- }
- rts, err := ro.Timestamp()
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.Query doesn't return a timestamp, error: %v", i, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: ReadOnlyTransaction.Query doesn't return expected timestamp: %v", i, err)
- }
- roTs := rts
- // ReadOnlyTransaction.Read
- got, err = readAll(ro.Read(ctx, "Singers", KeySets(Key{1}, Key{3}, Key{4}), []string{"SingerId", "FirstName", "LastName"}))
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.Read returns error %v, want nil", i, err)
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: got unexpected result from ReadOnlyTransaction.Read: %v, want %v", i, got, test.want)
- }
- rts, err = ro.Timestamp()
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.Read doesn't return a timestamp, error: %v", i, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: ReadOnlyTransaction.Read doesn't return expected timestamp: %v", i, err)
- }
- if roTs != rts {
- t.Errorf("%d: got two read timestamps: %v, %v, want ReadOnlyTransaction to return always the same read timestamp", i, roTs, rts)
- }
- // ReadOnlyTransaction.ReadRow
- got = nil
- for _, k := range []Key{Key{1}, Key{3}, Key{4}} {
- r, err := ro.ReadRow(ctx, "Singers", k, []string{"SingerId", "FirstName", "LastName"})
- if err != nil {
- continue
- }
- v, err := rowToValues(r)
- if err != nil {
- continue
- }
- got = append(got, v)
- rts, err = ro.Timestamp()
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.ReadRow(%v) doesn't return a timestamp, error: %v", i, k, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: ReadOnlyTransaction.ReadRow(%v) doesn't return expected timestamp: %v", i, k, err)
- }
- if roTs != rts {
- t.Errorf("%d: got two read timestamps: %v, %v, want ReadOnlyTransaction to return always the same read timestamp", i, roTs, rts)
- }
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: got unexpected results from ReadOnlyTransaction.ReadRow: %v, want %v", i, got, test.want)
- }
- // SingleUse.ReadUsingIndex
- got, err = readAll(ro.ReadUsingIndex(ctx, "Singers", "SingerByName", KeySets(Key{"Marc", "Foo"}, Key{"Alpha", "Beta"}, Key{"Last", "End"}), []string{"SingerId", "FirstName", "LastName"}))
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex returns error %v, want nil", i, err)
- }
- // The results from ReadUsingIndex is sorted by the index rather than primary key.
- if len(got) != len(test.want) {
- t.Errorf("%d: got unexpected result from ReadOnlyTransaction.ReadUsingIndex: %v, want %v", i, got, test.want)
- }
- for j, g := range got {
- if j > 0 {
- prev := got[j-1][1].(string) + got[j-1][2].(string)
- curr := got[j][1].(string) + got[j][2].(string)
- if strings.Compare(prev, curr) > 0 {
- t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex fails to order rows by index keys, %v should be after %v", i, got[j-1], got[j])
- }
- }
- found := false
- for _, w := range test.want {
- if reflect.DeepEqual(g, w) {
- found = true
- }
- }
- if !found {
- t.Errorf("%d: got unexpected result from ReadOnlyTransaction.ReadUsingIndex: %v, want %v", i, got, test.want)
- break
- }
- }
- rts, err = ro.Timestamp()
- if err != nil {
- t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex doesn't return a timestamp, error: %v", i, err)
- }
- if err := test.checkTs(rts); err != nil {
- t.Errorf("%d: ReadOnlyTransaction.ReadUsingIndex doesn't return expected timestamp: %v", i, err)
- }
- if roTs != rts {
- t.Errorf("%d: got two read timestamps: %v, %v, want ReadOnlyTransaction to return always the same read timestamp", i, roTs, rts)
- }
- ro.Close()
- }
-}
-
-// Test ReadWriteTransaction.
-func TestReadWriteTransaction(t *testing.T) {
- // Give a longer deadline because of transaction backoffs.
- ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
- defer cancel()
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- defer tearDown(ctx, t)
-
- // Set up two accounts
- accounts := []*Mutation{
- Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)}),
- Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(2), "Bar", int64(1)}),
- }
- if _, err := client.Apply(ctx, accounts, ApplyAtLeastOnce()); err != nil {
- t.Fatal(err)
- }
- wg := sync.WaitGroup{}
-
- readBalance := func(iter *RowIterator) (int64, error) {
- defer iter.Stop()
- var bal int64
- for {
- row, err := iter.Next()
- if err == iterator.Done {
- return bal, nil
- }
- if err != nil {
- return 0, err
- }
- if err := row.Column(0, &bal); err != nil {
- return 0, err
- }
- }
- }
-
- for i := 0; i < 20; i++ {
- wg.Add(1)
- go func(iter int) {
- defer wg.Done()
- _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error {
- // Query Foo's balance and Bar's balance.
- bf, e := readBalance(tx.Query(ctx,
- Statement{"SELECT Balance FROM Accounts WHERE AccountId = @id", map[string]interface{}{"id": int64(1)}}))
- if e != nil {
- return e
- }
- bb, e := readBalance(tx.Read(ctx, "Accounts", KeySets(Key{int64(2)}), []string{"Balance"}))
- if e != nil {
- return e
- }
- if bf <= 0 {
- return nil
- }
- bf--
- bb++
- tx.BufferWrite([]*Mutation{
- Update("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(1), bf}),
- Update("Accounts", []string{"AccountId", "Balance"}, []interface{}{int64(2), bb}),
- })
- return nil
- })
- if err != nil {
- t.Fatalf("%d: failed to execute transaction: %v", iter, err)
- }
- }(i)
- }
- // Because of context timeout, all goroutines will eventually return.
- wg.Wait()
- _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error {
- var bf, bb int64
- r, e := tx.ReadRow(ctx, "Accounts", Key{int64(1)}, []string{"Balance"})
- if e != nil {
- return e
- }
- if ce := r.Column(0, &bf); ce != nil {
- return ce
- }
- bb, e = readBalance(tx.ReadUsingIndex(ctx, "Accounts", "AccountByNickname", KeySets(Key{"Bar"}), []string{"Balance"}))
- if e != nil {
- return e
- }
- if bf != 30 || bb != 21 {
- t.Errorf("Foo's balance is now %v and Bar's balance is now %v, want %v and %v", bf, bb, 30, 21)
- }
- return nil
- })
- if err != nil {
- t.Errorf("failed to check balances: %v", err)
- }
-}
-
-const (
- testTable = "TestTable"
- testTableIndex = "TestTableByValue"
-)
-
-var testTableColumns = []string{"Key", "StringValue"}
-
-func TestReads(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
- // Set up testing environment.
- if err := prepare(ctx, t, readDBStatements); err != nil {
- // If prepare() fails, tear down whatever that's already up.
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- // After all tests, tear down testing environment.
- defer tearDown(ctx, t)
-
- // Includes k0..k14. Strings sort lexically, eg "k1" < "k10" < "k2".
- var ms []*Mutation
- for i := 0; i < 15; i++ {
- ms = append(ms, InsertOrUpdate(testTable,
- testTableColumns,
- []interface{}{fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i)}))
- }
- if _, err := client.Apply(ctx, ms, ApplyAtLeastOnce()); err != nil {
- t.Fatal(err)
- }
-
- // Empty read.
- rows, err := readAllTestTable(client.Single().Read(ctx, testTable,
- KeyRange{Start: Key{"k99"}, End: Key{"z"}}, testTableColumns))
- if err != nil {
- t.Fatal(err)
- }
- if got, want := len(rows), 0; got != want {
- t.Errorf("got %d, want %d", got, want)
- }
-
- // Index empty read.
- rows, err = readAllTestTable(client.Single().ReadUsingIndex(ctx, testTable, testTableIndex,
- KeyRange{Start: Key{"v99"}, End: Key{"z"}}, testTableColumns))
- if err != nil {
- t.Fatal(err)
- }
- if got, want := len(rows), 0; got != want {
- t.Errorf("got %d, want %d", got, want)
- }
-
- // Point read.
- row, err := client.Single().ReadRow(ctx, testTable, Key{"k1"}, testTableColumns)
- if err != nil {
- t.Fatal(err)
- }
- var got testTableRow
- if err := row.ToStruct(&got); err != nil {
- t.Fatal(err)
- }
- if want := (testTableRow{"k1", "v1"}); got != want {
- t.Errorf("got %v, want %v", got, want)
- }
-
- // Point read not found.
- _, err = client.Single().ReadRow(ctx, testTable, Key{"k999"}, testTableColumns)
- if ErrCode(err) != codes.NotFound {
- t.Fatalf("got %v, want NotFound", err)
- }
-
- // No index point read not found, because Go does not have ReadRowUsingIndex.
-
- rangeReads(ctx, t)
- indexRangeReads(ctx, t)
-}
-
-func rangeReads(ctx context.Context, t *testing.T) {
- checkRange := func(ks KeySet, wantNums ...int) {
- if msg, ok := compareRows(client.Single().Read(ctx, testTable, ks, testTableColumns), wantNums); !ok {
- t.Errorf("key set %+v: %s", ks, msg)
- }
- }
-
- checkRange(Key{"k1"}, 1)
- checkRange(KeyRange{Key{"k3"}, Key{"k5"}, ClosedOpen}, 3, 4)
- checkRange(KeyRange{Key{"k3"}, Key{"k5"}, ClosedClosed}, 3, 4, 5)
- checkRange(KeyRange{Key{"k3"}, Key{"k5"}, OpenClosed}, 4, 5)
- checkRange(KeyRange{Key{"k3"}, Key{"k5"}, OpenOpen}, 4)
-
- // Partial key specification.
- checkRange(KeyRange{Key{"k7"}, Key{}, ClosedClosed}, 7, 8, 9)
- checkRange(KeyRange{Key{"k7"}, Key{}, OpenClosed}, 8, 9)
- checkRange(KeyRange{Key{}, Key{"k11"}, ClosedOpen}, 0, 1, 10)
- checkRange(KeyRange{Key{}, Key{"k11"}, ClosedClosed}, 0, 1, 10, 11)
-
- // The following produce empty ranges.
- // TODO(jba): Consider a multi-part key to illustrate partial key behavior.
- // checkRange(KeyRange{Key{"k7"}, Key{}, ClosedOpen})
- // checkRange(KeyRange{Key{"k7"}, Key{}, OpenOpen})
- // checkRange(KeyRange{Key{}, Key{"k11"}, OpenOpen})
- // checkRange(KeyRange{Key{}, Key{"k11"}, OpenClosed})
-
- // Prefix is component-wise, not string prefix.
- checkRange(Key{"k1"}.AsPrefix(), 1)
- checkRange(KeyRange{Key{"k1"}, Key{"k2"}, ClosedOpen}, 1, 10, 11, 12, 13, 14)
-
- checkRange(AllKeys(), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
-}
-
-func indexRangeReads(ctx context.Context, t *testing.T) {
- checkRange := func(ks KeySet, wantNums ...int) {
- if msg, ok := compareRows(client.Single().ReadUsingIndex(ctx, testTable, testTableIndex, ks, testTableColumns),
- wantNums); !ok {
- t.Errorf("key set %+v: %s", ks, msg)
- }
- }
-
- checkRange(Key{"v1"}, 1)
- checkRange(KeyRange{Key{"v3"}, Key{"v5"}, ClosedOpen}, 3, 4)
- checkRange(KeyRange{Key{"v3"}, Key{"v5"}, ClosedClosed}, 3, 4, 5)
- checkRange(KeyRange{Key{"v3"}, Key{"v5"}, OpenClosed}, 4, 5)
- checkRange(KeyRange{Key{"v3"}, Key{"v5"}, OpenOpen}, 4)
-
- // // Partial key specification.
- checkRange(KeyRange{Key{"v7"}, Key{}, ClosedClosed}, 7, 8, 9)
- checkRange(KeyRange{Key{"v7"}, Key{}, OpenClosed}, 8, 9)
- checkRange(KeyRange{Key{}, Key{"v11"}, ClosedOpen}, 0, 1, 10)
- checkRange(KeyRange{Key{}, Key{"v11"}, ClosedClosed}, 0, 1, 10, 11)
-
- // // The following produce empty ranges.
- // checkRange(KeyRange{Key{"v7"}, Key{}, ClosedOpen})
- // checkRange(KeyRange{Key{"v7"}, Key{}, OpenOpen})
- // checkRange(KeyRange{Key{}, Key{"v11"}, OpenOpen})
- // checkRange(KeyRange{Key{}, Key{"v11"}, OpenClosed})
-
- // // Prefix is component-wise, not string prefix.
- checkRange(Key{"v1"}.AsPrefix(), 1)
- checkRange(KeyRange{Key{"v1"}, Key{"v2"}, ClosedOpen}, 1, 10, 11, 12, 13, 14)
- checkRange(AllKeys(), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
-
- // Read from an index with DESC ordering.
- wantNums := []int{14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
- if msg, ok := compareRows(client.Single().ReadUsingIndex(ctx, testTable, "TestTableByValueDesc", AllKeys(), testTableColumns),
- wantNums); !ok {
- t.Errorf("desc: %s", msg)
- }
-}
-
-func compareRows(iter *RowIterator, wantNums []int) (string, bool) {
- rows, err := readAllTestTable(iter)
- if err != nil {
- return err.Error(), false
- }
- want := map[string]string{}
- for _, n := range wantNums {
- want[fmt.Sprintf("k%d", n)] = fmt.Sprintf("v%d", n)
- }
- got := map[string]string{}
- for _, r := range rows {
- got[r.Key] = r.StringValue
- }
- if !reflect.DeepEqual(got, want) {
- return fmt.Sprintf("got %v, want %v", got, want), false
- }
- return "", true
-}
-
-func TestEarlyTimestamp(t *testing.T) {
- // Test that we can get the timestamp from a read-only transaction as
- // soon as we have read at least one row.
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
- // Set up testing environment.
- if err := prepare(ctx, t, readDBStatements); err != nil {
- // If prepare() fails, tear down whatever that's already up.
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- // After all tests, tear down testing environment.
- defer tearDown(ctx, t)
-
- var ms []*Mutation
- for i := 0; i < 3; i++ {
- ms = append(ms, InsertOrUpdate(testTable,
- testTableColumns,
- []interface{}{fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i)}))
- }
- if _, err := client.Apply(ctx, ms, ApplyAtLeastOnce()); err != nil {
- t.Fatal(err)
- }
-
- txn := client.Single()
- iter := txn.Read(ctx, testTable, AllKeys(), testTableColumns)
- defer iter.Stop()
- // In single-use transaction, we should get an error before reading anything.
- if _, err := txn.Timestamp(); err == nil {
- t.Error("wanted error, got nil")
- }
- // After reading one row, the timestamp should be available.
- _, err := iter.Next()
- if err != nil {
- t.Fatal(err)
- }
- if _, err := txn.Timestamp(); err != nil {
- t.Errorf("got %v, want nil", err)
- }
-
- txn = client.ReadOnlyTransaction()
- defer txn.Close()
- iter = txn.Read(ctx, testTable, AllKeys(), testTableColumns)
- defer iter.Stop()
- // In an ordinary read-only transaction, the timestamp should be
- // available immediately.
- if _, err := txn.Timestamp(); err != nil {
- t.Errorf("got %v, want nil", err)
- }
-}
-
-func TestNestedTransaction(t *testing.T) {
- // You cannot use a transaction from inside a read-write transaction.
- ctx := context.Background()
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- defer tearDown(ctx, t)
- client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error {
- _, err := client.ReadWriteTransaction(ctx,
- func(context.Context, *ReadWriteTransaction) error { return nil })
- if ErrCode(err) != codes.FailedPrecondition {
- t.Fatalf("got %v, want FailedPrecondition", err)
- }
- _, err = client.Single().ReadRow(ctx, "Singers", Key{1}, []string{"SingerId"})
- if ErrCode(err) != codes.FailedPrecondition {
- t.Fatalf("got %v, want FailedPrecondition", err)
- }
- rot := client.ReadOnlyTransaction()
- defer rot.Close()
- _, err = rot.ReadRow(ctx, "Singers", Key{1}, []string{"SingerId"})
- if ErrCode(err) != codes.FailedPrecondition {
- t.Fatalf("got %v, want FailedPrecondition", err)
- }
- return nil
- })
-}
-
-// Test client recovery on database recreation.
-func TestDbRemovalRecovery(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- defer tearDown(ctx, t)
-
- // Drop the testing database.
- if err := admin.DropDatabase(ctx, &adminpb.DropDatabaseRequest{db}); err != nil {
- t.Fatalf("failed to drop testing database %v: %v", db, err)
- }
-
- // Now, send the query.
- iter := client.Single().Query(ctx, Statement{SQL: "SELECT SingerId FROM Singers"})
- defer iter.Stop()
- if _, err := iter.Next(); err == nil {
- t.Errorf("client sends query to removed database successfully, want it to fail")
- }
-
- // Recreate database and table.
- op, err := admin.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
- Parent: fmt.Sprintf("projects/%v/instances/%v", testProjectID, testInstanceID),
- CreateStatement: "CREATE DATABASE " + dbName,
- ExtraStatements: []string{
- `CREATE TABLE Singers (
- SingerId INT64 NOT NULL,
- FirstName STRING(1024),
- LastName STRING(1024),
- SingerInfo BYTES(MAX)
- ) PRIMARY KEY (SingerId)`,
- },
- })
- if _, err := op.Wait(ctx); err != nil {
- t.Errorf("cannot recreate testing DB %v: %v", db, err)
- }
-
- // Now, send the query again.
- iter = client.Single().Query(ctx, Statement{SQL: "SELECT SingerId FROM Singers"})
- defer iter.Stop()
- _, err = iter.Next()
- if err != nil && err != iterator.Done {
- t.Fatalf("failed to send query to database %v: %v", db, err)
- }
-}
-
-// Test encoding/decoding non-struct Cloud Spanner types.
-func TestBasicTypes(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- defer tearDown(ctx, t)
- t1, _ := time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z")
- // Boundaries
- t2, _ := time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00.000000000Z")
- t3, _ := time.Parse(time.RFC3339Nano, "9999-12-31T23:59:59.999999999Z")
- d1, _ := civil.ParseDate("2016-11-15")
- // Boundaries
- d2, _ := civil.ParseDate("0001-01-01")
- d3, _ := civil.ParseDate("9999-12-31")
-
- tests := []struct {
- col string
- val interface{}
- want interface{}
- }{
- {col: "String", val: ""},
- {col: "String", val: "", want: NullString{"", true}},
- {col: "String", val: "foo"},
- {col: "String", val: "foo", want: NullString{"foo", true}},
- {col: "String", val: NullString{"bar", true}, want: "bar"},
- {col: "String", val: NullString{"bar", false}, want: NullString{"", false}},
- {col: "StringArray", val: []string(nil), want: []NullString(nil)},
- {col: "StringArray", val: []string{}, want: []NullString{}},
- {col: "StringArray", val: []string{"foo", "bar"}, want: []NullString{{"foo", true}, {"bar", true}}},
- {col: "StringArray", val: []NullString(nil)},
- {col: "StringArray", val: []NullString{}},
- {col: "StringArray", val: []NullString{{"foo", true}, {}}},
- {col: "Bytes", val: []byte{}},
- {col: "Bytes", val: []byte{1, 2, 3}},
- {col: "Bytes", val: []byte(nil)},
- {col: "BytesArray", val: [][]byte(nil)},
- {col: "BytesArray", val: [][]byte{}},
- {col: "BytesArray", val: [][]byte{[]byte{1}, []byte{2, 3}}},
- {col: "Int64a", val: 0, want: int64(0)},
- {col: "Int64a", val: -1, want: int64(-1)},
- {col: "Int64a", val: 2, want: int64(2)},
- {col: "Int64a", val: int64(3)},
- {col: "Int64a", val: 4, want: NullInt64{4, true}},
- {col: "Int64a", val: NullInt64{5, true}, want: int64(5)},
- {col: "Int64a", val: NullInt64{6, true}, want: int64(6)},
- {col: "Int64a", val: NullInt64{7, false}, want: NullInt64{0, false}},
- {col: "Int64Array", val: []int(nil), want: []NullInt64(nil)},
- {col: "Int64Array", val: []int{}, want: []NullInt64{}},
- {col: "Int64Array", val: []int{1, 2}, want: []NullInt64{{1, true}, {2, true}}},
- {col: "Int64Array", val: []int64(nil), want: []NullInt64(nil)},
- {col: "Int64Array", val: []int64{}, want: []NullInt64{}},
- {col: "Int64Array", val: []int64{1, 2}, want: []NullInt64{{1, true}, {2, true}}},
- {col: "Int64Array", val: []NullInt64(nil)},
- {col: "Int64Array", val: []NullInt64{}},
- {col: "Int64Array", val: []NullInt64{{1, true}, {}}},
- {col: "Bool", val: false},
- {col: "Bool", val: true},
- {col: "Bool", val: false, want: NullBool{false, true}},
- {col: "Bool", val: true, want: NullBool{true, true}},
- {col: "Bool", val: NullBool{true, true}},
- {col: "Bool", val: NullBool{false, false}},
- {col: "BoolArray", val: []bool(nil), want: []NullBool(nil)},
- {col: "BoolArray", val: []bool{}, want: []NullBool{}},
- {col: "BoolArray", val: []bool{true, false}, want: []NullBool{{true, true}, {false, true}}},
- {col: "BoolArray", val: []NullBool(nil)},
- {col: "BoolArray", val: []NullBool{}},
- {col: "BoolArray", val: []NullBool{{false, true}, {true, true}, {}}},
- {col: "Float64", val: 0.0},
- {col: "Float64", val: 3.14},
- {col: "Float64", val: math.NaN()},
- {col: "Float64", val: math.Inf(1)},
- {col: "Float64", val: math.Inf(-1)},
- {col: "Float64", val: 2.78, want: NullFloat64{2.78, true}},
- {col: "Float64", val: NullFloat64{2.71, true}, want: 2.71},
- {col: "Float64", val: NullFloat64{1.41, true}, want: NullFloat64{1.41, true}},
- {col: "Float64", val: NullFloat64{0, false}},
- {col: "Float64Array", val: []float64(nil), want: []NullFloat64(nil)},
- {col: "Float64Array", val: []float64{}, want: []NullFloat64{}},
- {col: "Float64Array", val: []float64{2.72, 3.14, math.Inf(1)}, want: []NullFloat64{{2.72, true}, {3.14, true}, {math.Inf(1), true}}},
- {col: "Float64Array", val: []NullFloat64(nil)},
- {col: "Float64Array", val: []NullFloat64{}},
- {col: "Float64Array", val: []NullFloat64{{2.72, true}, {math.Inf(1), true}, {}}},
- {col: "Date", val: d1},
- {col: "Date", val: d1, want: NullDate{d1, true}},
- {col: "Date", val: NullDate{d1, true}},
- {col: "Date", val: NullDate{d1, true}, want: d1},
- {col: "Date", val: NullDate{civil.Date{}, false}},
- {col: "DateArray", val: []civil.Date(nil), want: []NullDate(nil)},
- {col: "DateArray", val: []civil.Date{}, want: []NullDate{}},
- {col: "DateArray", val: []civil.Date{d1, d2, d3}, want: []NullDate{{d1, true}, {d2, true}, {d3, true}}},
- {col: "Timestamp", val: t1},
- {col: "Timestamp", val: t1, want: NullTime{t1, true}},
- {col: "Timestamp", val: NullTime{t1, true}},
- {col: "Timestamp", val: NullTime{t1, true}, want: t1},
- {col: "Timestamp", val: NullTime{}},
- {col: "TimestampArray", val: []time.Time(nil), want: []NullTime(nil)},
- {col: "TimestampArray", val: []time.Time{}, want: []NullTime{}},
- {col: "TimestampArray", val: []time.Time{t1, t2, t3}, want: []NullTime{{t1, true}, {t2, true}, {t3, true}}},
- }
-
- // Write rows into table first.
- var muts []*Mutation
- for i, test := range tests {
- muts = append(muts, InsertOrUpdate("Types", []string{"RowID", test.col}, []interface{}{i, test.val}))
- }
- if _, err := client.Apply(ctx, muts, ApplyAtLeastOnce()); err != nil {
- t.Fatal(err)
- }
-
- for i, test := range tests {
- row, err := client.Single().ReadRow(ctx, "Types", []interface{}{i}, []string{test.col})
- if err != nil {
- t.Fatalf("Unable to fetch row %v: %v", i, err)
- }
- // Create new instance of type of test.want.
- want := test.want
- if want == nil {
- want = test.val
- }
- gotp := reflect.New(reflect.TypeOf(want))
- if err := row.Column(0, gotp.Interface()); err != nil {
- t.Errorf("%d: col:%v val:%#v, %v", i, test.col, test.val, err)
- continue
- }
- got := reflect.Indirect(gotp).Interface()
-
- // One of the test cases is checking NaN handling. Given
- // NaN!=NaN, we can't use reflect to test for it.
- isNaN := func(t interface{}) bool {
- f, ok := t.(float64)
- if !ok {
- return false
- }
- return math.IsNaN(f)
- }
- if isNaN(got) && isNaN(want) {
- continue
- }
-
- // Check non-NaN cases.
- if !reflect.DeepEqual(got, want) {
- t.Errorf("%d: col:%v val:%#v, got %#v, want %#v", i, test.col, test.val, got, want)
- continue
- }
- }
-}
-
-// Test decoding Cloud Spanner STRUCT type.
-func TestStructTypes(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
- if err := prepare(ctx, t, singerDBStatements); err != nil {
- tearDown(ctx, t)
- t.Fatalf("cannot set up testing environment: %v", err)
- }
- defer tearDown(ctx, t)
-
- tests := []struct {
- q Statement
- want func(r *Row) error
- }{
- {
- q: Statement{SQL: `SELECT ARRAY(SELECT STRUCT(1, 2))`},
- want: func(r *Row) error {
- // Test STRUCT ARRAY decoding to []NullRow.
- var rows []NullRow
- if err := r.Column(0, &rows); err != nil {
- return err
- }
- if len(rows) != 1 {
- return fmt.Errorf("len(rows) = %d; want 1", len(rows))
- }
- if !rows[0].Valid {
- return fmt.Errorf("rows[0] is NULL")
- }
- var i, j int64
- if err := rows[0].Row.Columns(&i, &j); err != nil {
- return err
- }
- if i != 1 || j != 2 {
- return fmt.Errorf("got (%d,%d), want (1,2)", i, j)
- }
- return nil
- },
- },
- {
- q: Statement{SQL: `SELECT ARRAY(SELECT STRUCT(1 as foo, 2 as bar)) as col1`},
- want: func(r *Row) error {
- // Test Row.ToStruct.
- s := struct {
- Col1 []*struct {
- Foo int64 `spanner:"foo"`
- Bar int64 `spanner:"bar"`
- } `spanner:"col1"`
- }{}
- if err := r.ToStruct(&s); err != nil {
- return err
- }
- want := struct {
- Col1 []*struct {
- Foo int64 `spanner:"foo"`
- Bar int64 `spanner:"bar"`
- } `spanner:"col1"`
- }{
- Col1: []*struct {
- Foo int64 `spanner:"foo"`
- Bar int64 `spanner:"bar"`
- }{
- {
- Foo: 1,
- Bar: 2,
- },
- },
- }
- if !reflect.DeepEqual(want, s) {
- return fmt.Errorf("unexpected decoding result: %v, want %v", s, want)
- }
- return nil
- },
- },
- }
- for i, test := range tests {
- iter := client.Single().Query(ctx, test.q)
- defer iter.Stop()
- row, err := iter.Next()
- if err != nil {
- t.Errorf("%d: %v", i, err)
- continue
- }
- if err := test.want(row); err != nil {
- t.Errorf("%d: %v", i, err)
- continue
- }
- }
-}
-
-func rowToValues(r *Row) ([]interface{}, error) {
- var x int64
- var y, z string
- if err := r.Column(0, &x); err != nil {
- return nil, err
- }
- if err := r.Column(1, &y); err != nil {
- return nil, err
- }
- if err := r.Column(2, &z); err != nil {
- return nil, err
- }
- return []interface{}{x, y, z}, nil
-}
-
-func readAll(iter *RowIterator) ([][]interface{}, error) {
- defer iter.Stop()
- var vals [][]interface{}
- for {
- row, err := iter.Next()
- if err == iterator.Done {
- return vals, nil
- }
- if err != nil {
- return nil, err
- }
- v, err := rowToValues(row)
- if err != nil {
- return nil, err
- }
- vals = append(vals, v)
- }
-}
-
-func readAllTestTable(iter *RowIterator) ([]testTableRow, error) {
- defer iter.Stop()
- var vals []testTableRow
- for {
- row, err := iter.Next()
- if err == iterator.Done {
- return vals, nil
- }
- if err != nil {
- return nil, err
- }
- var ttr testTableRow
- if err := row.ToStruct(&ttr); err != nil {
- return nil, err
- }
- vals = append(vals, ttr)
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/statement.go b/vendor/cloud.google.com/go/spanner/statement.go
deleted file mode 100644
index 8e422b09c..000000000
--- a/vendor/cloud.google.com/go/spanner/statement.go
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
-
- proto3 "github.com/golang/protobuf/ptypes/struct"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc/codes"
-)
-
-// A Statement is a SQL query with named parameters.
-//
-// A parameter placeholder consists of '@' followed by the parameter name.
-// Parameter names consist of any combination of letters, numbers, and
-// underscores. Names may be entirely numeric (e.g., "WHERE m.id = @5").
-// Parameters may appear anywhere that a literal value is expected. The same
-// parameter name may be used more than once. It is an error to execute a
-// statement with unbound parameters. On the other hand, it is allowable to
-// bind parameter names that are not used.
-//
-// See the documentation of the Row type for how Go types are mapped to Cloud
-// Spanner types.
-type Statement struct {
- SQL string
- Params map[string]interface{}
-}
-
-// NewStatement returns a Statement with the given SQL and an empty Params map.
-func NewStatement(sql string) Statement {
- return Statement{SQL: sql, Params: map[string]interface{}{}}
-}
-
-// errBindParam returns error for not being able to bind parameter to query request.
-func errBindParam(k string, v interface{}, err error) error {
- if err == nil {
- return nil
- }
- se, ok := toSpannerError(err).(*Error)
- if !ok {
- return spannerErrorf(codes.InvalidArgument, "failed to bind query parameter(name: %q, value: %q), error = <%v>", k, v, err)
- }
- se.decorate(fmt.Sprintf("failed to bind query parameter(name: %q, value: %q)", k, v))
- return se
-}
-
-// bindParams binds parameters in a Statement to a sppb.ExecuteSqlRequest.
-func (s *Statement) bindParams(r *sppb.ExecuteSqlRequest) error {
- r.Params = &proto3.Struct{
- Fields: map[string]*proto3.Value{},
- }
- r.ParamTypes = map[string]*sppb.Type{}
- for k, v := range s.Params {
- val, t, err := encodeValue(v)
- if err != nil {
- return errBindParam(k, v, err)
- }
- r.Params.Fields[k] = val
- r.ParamTypes[k] = t
- }
- return nil
-}
diff --git a/vendor/cloud.google.com/go/spanner/statement_test.go b/vendor/cloud.google.com/go/spanner/statement_test.go
deleted file mode 100644
index a441e0e82..000000000
--- a/vendor/cloud.google.com/go/spanner/statement_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "reflect"
- "testing"
-
- proto3 "github.com/golang/protobuf/ptypes/struct"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// Test Statement.bindParams.
-func TestBindParams(t *testing.T) {
- // Verify Statement.bindParams generates correct values and types.
- want := sppb.ExecuteSqlRequest{
- Params: &proto3.Struct{
- Fields: map[string]*proto3.Value{
- "var1": stringProto("abc"),
- "var2": intProto(1),
- },
- },
- ParamTypes: map[string]*sppb.Type{
- "var1": stringType(),
- "var2": intType(),
- },
- }
- st := Statement{
- SQL: "SELECT id from t_foo WHERE col1 = @var1 AND col2 = @var2",
- Params: map[string]interface{}{"var1": "abc", "var2": int64(1)},
- }
- got := sppb.ExecuteSqlRequest{}
- if err := st.bindParams(&got); err != nil || !reflect.DeepEqual(got, want) {
- t.Errorf("bind result: \n(%v, %v)\nwant\n(%v, %v)\n", got, err, want, nil)
- }
- // Verify type error reporting.
- st.Params["var2"] = struct{}{}
- wantErr := errBindParam("var2", struct{}{}, errEncoderUnsupportedType(struct{}{}))
- if err := st.bindParams(&got); !reflect.DeepEqual(err, wantErr) {
- t.Errorf("got unexpected error: %v, want: %v", err, wantErr)
- }
-}
-
-func TestNewStatement(t *testing.T) {
- s := NewStatement("query")
- if got, want := s.SQL, "query"; got != want {
- t.Errorf("got %q, want %q", got, want)
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/timestampbound.go b/vendor/cloud.google.com/go/spanner/timestampbound.go
deleted file mode 100644
index 068d96600..000000000
--- a/vendor/cloud.google.com/go/spanner/timestampbound.go
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "fmt"
- "time"
-
- pbd "github.com/golang/protobuf/ptypes/duration"
- pbt "github.com/golang/protobuf/ptypes/timestamp"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// timestampBoundType specifies the timestamp bound mode.
-type timestampBoundType int
-
-const (
- strong timestampBoundType = iota // strong reads
- exactStaleness // read with exact staleness
- maxStaleness // read with max staleness
- minReadTimestamp // read with min freshness
- readTimestamp // read data at exact timestamp
-)
-
-// TimestampBound defines how Cloud Spanner will choose a timestamp for a single
-// read/query or read-only transaction.
-//
-// 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. A TimestampBound
-// can be specified when creating transactions, see the documentation of
-// spanner.Client for an example.
-//
-// Strong reads
-//
-// 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.
-//
-// Use StrongRead() to create a bound of this type.
-//
-// 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 less than or equal to 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.
-//
-// Use ReadTimestamp() and ExactStaleness() to create a bound of this type.
-//
-// 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 reads and single-use
-// read-only transactions.
-//
-// Use MinReadTimestamp() and MaxStaleness() to create a bound of this type.
-//
-// 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 four hours
-// old. Because of this, Cloud Spanner cannot perform reads at read timestamps more
-// than four hours 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 ErrorCode.FAILED_PRECONDITION.
-type TimestampBound struct {
- mode timestampBoundType
- d time.Duration
- t time.Time
-}
-
-// StrongRead returns a TimestampBound that will perform reads and queries at a
-// timestamp where all previously committed transactions are visible.
-func StrongRead() TimestampBound {
- return TimestampBound{mode: strong}
-}
-
-// ExactStaleness returns a TimestampBound that will perform reads and queries
-// at an exact staleness.
-func ExactStaleness(d time.Duration) TimestampBound {
- return TimestampBound{
- mode: exactStaleness,
- d: d,
- }
-}
-
-// MaxStaleness returns a TimestampBound that will perform reads and queries at
-// a time chosen to be at most "d" stale.
-func MaxStaleness(d time.Duration) TimestampBound {
- return TimestampBound{
- mode: maxStaleness,
- d: d,
- }
-}
-
-// MinReadTimestamp returns a TimestampBound that bound that will perform reads
-// and queries at a time chosen to be at least "t".
-func MinReadTimestamp(t time.Time) TimestampBound {
- return TimestampBound{
- mode: minReadTimestamp,
- t: t,
- }
-}
-
-// ReadTimestamp returns a TimestampBound that will peform reads and queries at
-// the given time.
-func ReadTimestamp(t time.Time) TimestampBound {
- return TimestampBound{
- mode: readTimestamp,
- t: t,
- }
-}
-
-// String implements fmt.Stringer.
-func (tb TimestampBound) String() string {
- switch tb.mode {
- case strong:
- return fmt.Sprintf("(strong)")
- case exactStaleness:
- return fmt.Sprintf("(exactStaleness: %s)", tb.d)
- case maxStaleness:
- return fmt.Sprintf("(maxStaleness: %s)", tb.d)
- case minReadTimestamp:
- return fmt.Sprintf("(minReadTimestamp: %s)", tb.t)
- case readTimestamp:
- return fmt.Sprintf("(readTimestamp: %s)", tb.t)
- default:
- return fmt.Sprintf("{mode=%v, d=%v, t=%v}", tb.mode, tb.d, tb.t)
- }
-}
-
-// durationProto takes a time.Duration and converts it into pdb.Duration for
-// calling gRPC APIs.
-func durationProto(d time.Duration) *pbd.Duration {
- n := d.Nanoseconds()
- return &pbd.Duration{
- Seconds: n / int64(time.Second),
- Nanos: int32(n % int64(time.Second)),
- }
-}
-
-// timestampProto takes a time.Time and converts it into pbt.Timestamp for calling
-// gRPC APIs.
-func timestampProto(t time.Time) *pbt.Timestamp {
- return &pbt.Timestamp{
- Seconds: t.Unix(),
- Nanos: int32(t.Nanosecond()),
- }
-}
-
-// buildTransactionOptionsReadOnly converts a spanner.TimestampBound into a sppb.TransactionOptions_ReadOnly
-// transaction option, which is then used in transactional reads.
-func buildTransactionOptionsReadOnly(tb TimestampBound, returnReadTimestamp bool) *sppb.TransactionOptions_ReadOnly {
- pb := &sppb.TransactionOptions_ReadOnly{
- ReturnReadTimestamp: returnReadTimestamp,
- }
- switch tb.mode {
- case strong:
- pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_Strong{
- Strong: true,
- }
- case exactStaleness:
- pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ExactStaleness{
- ExactStaleness: durationProto(tb.d),
- }
- case maxStaleness:
- pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MaxStaleness{
- MaxStaleness: durationProto(tb.d),
- }
- case minReadTimestamp:
- pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{
- MinReadTimestamp: timestampProto(tb.t),
- }
- case readTimestamp:
- pb.TimestampBound = &sppb.TransactionOptions_ReadOnly_ReadTimestamp{
- ReadTimestamp: timestampProto(tb.t),
- }
- default:
- panic(fmt.Sprintf("buildTransactionOptionsReadOnly(%v,%v)", tb, returnReadTimestamp))
- }
- return pb
-}
diff --git a/vendor/cloud.google.com/go/spanner/timestampbound_test.go b/vendor/cloud.google.com/go/spanner/timestampbound_test.go
deleted file mode 100644
index 47fb481db..000000000
--- a/vendor/cloud.google.com/go/spanner/timestampbound_test.go
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "reflect"
- "testing"
- "time"
-
- pbd "github.com/golang/protobuf/ptypes/duration"
- pbt "github.com/golang/protobuf/ptypes/timestamp"
-
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-// Test generating TimestampBound for strong reads.
-func TestStrong(t *testing.T) {
- got := StrongRead()
- want := TimestampBound{mode: strong}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("Strong() = %v; want %v", got, want)
- }
-}
-
-// Test generating TimestampBound for reads with exact staleness.
-func TestExactStaleness(t *testing.T) {
- got := ExactStaleness(10 * time.Second)
- want := TimestampBound{mode: exactStaleness, d: 10 * time.Second}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("ExactStaleness(10*time.Second) = %v; want %v", got, want)
- }
-}
-
-// Test generating TimestampBound for reads with max staleness.
-func TestMaxStaleness(t *testing.T) {
- got := MaxStaleness(10 * time.Second)
- want := TimestampBound{mode: maxStaleness, d: 10 * time.Second}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("MaxStaleness(10*time.Second) = %v; want %v", got, want)
- }
-}
-
-// Test generating TimestampBound for reads with minimum freshness requirement.
-func TestMinReadTimestamp(t *testing.T) {
- ts := time.Now()
- got := MinReadTimestamp(ts)
- want := TimestampBound{mode: minReadTimestamp, t: ts}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("MinReadTimestamp(%v) = %v; want %v", ts, got, want)
- }
-}
-
-// Test generating TimestampBound for reads requesting data at a exact timestamp.
-func TestReadTimestamp(t *testing.T) {
- ts := time.Now()
- got := ReadTimestamp(ts)
- want := TimestampBound{mode: readTimestamp, t: ts}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("ReadTimestamp(%v) = %v; want %v", ts, got, want)
- }
-}
-
-// Test TimestampBound.String.
-func TestTimestampBoundString(t *testing.T) {
- ts := time.Unix(1136239445, 0).UTC()
- var tests = []struct {
- tb TimestampBound
- want string
- }{
- {
- tb: TimestampBound{mode: strong},
- want: "(strong)",
- },
- {
- tb: TimestampBound{mode: exactStaleness, d: 10 * time.Second},
- want: "(exactStaleness: 10s)",
- },
- {
- tb: TimestampBound{mode: maxStaleness, d: 10 * time.Second},
- want: "(maxStaleness: 10s)",
- },
- {
- tb: TimestampBound{mode: minReadTimestamp, t: ts},
- want: "(minReadTimestamp: 2006-01-02 22:04:05 +0000 UTC)",
- },
- {
- tb: TimestampBound{mode: readTimestamp, t: ts},
- want: "(readTimestamp: 2006-01-02 22:04:05 +0000 UTC)",
- },
- }
- for _, test := range tests {
- got := test.tb.String()
- if got != test.want {
- t.Errorf("%#v.String():\ngot %q\nwant %q", test.tb, got, test.want)
- }
- }
-}
-
-// Test time.Duration to pdb.Duration conversion.
-func TestDurationProto(t *testing.T) {
- var tests = []struct {
- d time.Duration
- want pbd.Duration
- }{
- {time.Duration(0), pbd.Duration{Seconds: 0, Nanos: 0}},
- {time.Second, pbd.Duration{Seconds: 1, Nanos: 0}},
- {time.Millisecond, pbd.Duration{Seconds: 0, Nanos: 1e6}},
- {15 * time.Nanosecond, pbd.Duration{Seconds: 0, Nanos: 15}},
- {42 * time.Hour, pbd.Duration{Seconds: 151200}},
- {-(1*time.Hour + 4*time.Millisecond), pbd.Duration{Seconds: -3600, Nanos: -4e6}},
- }
- for _, test := range tests {
- got := durationProto(test.d)
- if !reflect.DeepEqual(got, &test.want) {
- t.Errorf("durationProto(%v) = %v; want %v", test.d, got, test.want)
- }
- }
-}
-
-// Test time.Time to pbt.Timestamp conversion.
-func TestTimeProto(t *testing.T) {
- var tests = []struct {
- t time.Time
- want pbt.Timestamp
- }{
- {time.Unix(0, 0), pbt.Timestamp{}},
- {time.Unix(1136239445, 12345), pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}},
- {time.Unix(-1000, 12345), pbt.Timestamp{Seconds: -1000, Nanos: 12345}},
- }
- for _, test := range tests {
- got := timestampProto(test.t)
- if !reflect.DeepEqual(got, &test.want) {
- t.Errorf("timestampProto(%v) = %v; want %v", test.t, got, test.want)
- }
- }
-}
-
-// Test readonly transaction option builder.
-func TestBuildTransactionOptionsReadOnly(t *testing.T) {
- ts := time.Unix(1136239445, 12345)
- var tests = []struct {
- tb TimestampBound
- ts bool
- want sppb.TransactionOptions_ReadOnly
- }{
- {
- StrongRead(), false,
- sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{
- Strong: true},
- ReturnReadTimestamp: false,
- },
- },
- {
- ExactStaleness(10 * time.Second), true,
- sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_ExactStaleness{
- ExactStaleness: &pbd.Duration{Seconds: 10}},
- ReturnReadTimestamp: true,
- },
- },
- {
- MaxStaleness(10 * time.Second), true,
- sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_MaxStaleness{
- MaxStaleness: &pbd.Duration{Seconds: 10}},
- ReturnReadTimestamp: true,
- },
- },
-
- {
- MinReadTimestamp(ts), true,
- sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{
- MinReadTimestamp: &pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}},
- ReturnReadTimestamp: true,
- },
- },
- {
- ReadTimestamp(ts), true,
- sppb.TransactionOptions_ReadOnly{
- TimestampBound: &sppb.TransactionOptions_ReadOnly_ReadTimestamp{
- ReadTimestamp: &pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}},
- ReturnReadTimestamp: true,
- },
- },
- }
- for _, test := range tests {
- got := buildTransactionOptionsReadOnly(test.tb, test.ts)
- if !reflect.DeepEqual(got, &test.want) {
- t.Errorf("buildTransactionOptionsReadOnly(%v,%v) = %v; want %v", test.tb, test.ts, got, test.want)
- }
- }
-}
diff --git a/vendor/cloud.google.com/go/spanner/transaction.go b/vendor/cloud.google.com/go/spanner/transaction.go
deleted file mode 100644
index ff1e96fe9..000000000
--- a/vendor/cloud.google.com/go/spanner/transaction.go
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "sync"
- "time"
-
- "golang.org/x/net/context"
-
- "google.golang.org/api/iterator"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
-)
-
-// transactionID stores a transaction ID which uniquely identifies a transaction in Cloud Spanner.
-type transactionID []byte
-
-// txReadEnv manages a read-transaction environment consisting of a session handle and a transaction selector.
-type txReadEnv interface {
- // acquire returns a read-transaction environment that can be used to perform a transactional read.
- acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error)
- // sets the transaction's read timestamp
- setTimestamp(time.Time)
- // release should be called at the end of every transactional read to deal with session recycling.
- release(error)
-}
-
-// txReadOnly contains methods for doing transactional reads.
-type txReadOnly struct {
- // read-transaction environment for performing transactional read operations.
- txReadEnv
-}
-
-// errSessionClosed returns error for using a recycled/destroyed session
-func errSessionClosed(sh *sessionHandle) error {
- return spannerErrorf(codes.FailedPrecondition,
- "session is already recycled / destroyed: session_id = %q, rpc_client = %v", sh.getID(), sh.getClient())
-}
-
-// Read returns a RowIterator for reading multiple rows from the database.
-func (t *txReadOnly) Read(ctx context.Context, table string, keys KeySet, columns []string) *RowIterator {
- // ReadUsingIndex will use primary index if an empty index name is provided.
- return t.ReadUsingIndex(ctx, table, "", keys, columns)
-}
-
-// ReadUsingIndex returns a RowIterator for reading multiple rows from the database
-// using an index.
-//
-// Currently, this function can only read columns that are part of the index
-// key, part of the primary key, or stored in the index due to a STORING clause
-// in the index definition.
-func (t *txReadOnly) ReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string) *RowIterator {
- var (
- sh *sessionHandle
- ts *sppb.TransactionSelector
- err error
- )
- kset, err := keys.keySetProto()
- if err != nil {
- return &RowIterator{err: err}
- }
- if sh, ts, err = t.acquire(ctx); err != nil {
- return &RowIterator{err: err}
- }
- // Cloud Spanner will return "Session not found" on bad sessions.
- sid, client := sh.getID(), sh.getClient()
- if sid == "" || client == nil {
- // Might happen if transaction is closed in the middle of a API call.
- return &RowIterator{err: errSessionClosed(sh)}
- }
- return stream(
- contextWithOutgoingMetadata(ctx, sh.getMetadata()),
- func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) {
- return client.StreamingRead(ctx,
- &sppb.ReadRequest{
- Session: sid,
- Transaction: ts,
- Table: table,
- Index: index,
- Columns: columns,
- KeySet: kset,
- ResumeToken: resumeToken,
- })
- },
- t.setTimestamp,
- t.release,
- )
-}
-
-// errRowNotFound returns error for not being able to read the row identified by key.
-func errRowNotFound(table string, key Key) error {
- return spannerErrorf(codes.NotFound, "row not found(Table: %v, PrimaryKey: %v)", table, key)
-}
-
-// ReadRow reads a single row from the database.
-//
-// If no row is present with the given key, then ReadRow returns an error where
-// spanner.ErrCode(err) is codes.NotFound.
-func (t *txReadOnly) ReadRow(ctx context.Context, table string, key Key, columns []string) (*Row, error) {
- iter := t.Read(ctx, table, key, columns)
- defer iter.Stop()
- row, err := iter.Next()
- switch err {
- case iterator.Done:
- return nil, errRowNotFound(table, key)
- case nil:
- return row, nil
- default:
- return nil, err
- }
-}
-
-// Query executes a query against the database. It returns a RowIterator
-// for retrieving the resulting rows.
-func (t *txReadOnly) Query(ctx context.Context, statement Statement) *RowIterator {
- var (
- sh *sessionHandle
- ts *sppb.TransactionSelector
- err error
- )
- if sh, ts, err = t.acquire(ctx); err != nil {
- return &RowIterator{err: err}
- }
- // Cloud Spanner will return "Session not found" on bad sessions.
- sid, client := sh.getID(), sh.getClient()
- if sid == "" || client == nil {
- // Might happen if transaction is closed in the middle of a API call.
- return &RowIterator{err: errSessionClosed(sh)}
- }
- req := &sppb.ExecuteSqlRequest{
- Session: sid,
- Transaction: ts,
- Sql: statement.SQL,
- }
- if err := statement.bindParams(req); err != nil {
- return &RowIterator{err: err}
- }
- return stream(
- contextWithOutgoingMetadata(ctx, sh.getMetadata()),
- func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) {
- req.ResumeToken = resumeToken
- return client.ExecuteStreamingSql(ctx, req)
- },
- t.setTimestamp,
- t.release)
-}
-
-// txState is the status of a transaction.
-type txState int
-
-const (
- // transaction is new, waiting to be initialized.
- txNew txState = iota
- // transaction is being initialized.
- txInit
- // transaction is active and can perform read/write.
- txActive
- // transaction is closed, cannot be used anymore.
- txClosed
-)
-
-// errRtsUnavailable returns error for read transaction's read timestamp being unavailable.
-func errRtsUnavailable() error {
- return spannerErrorf(codes.Internal, "read timestamp is unavailable")
-}
-
-// errTxNotInitialized returns error for using an uninitialized transaction.
-func errTxNotInitialized() error {
- return spannerErrorf(codes.InvalidArgument, "cannot use a uninitialized transaction")
-}
-
-// errTxClosed returns error for using a closed transaction.
-func errTxClosed() error {
- return spannerErrorf(codes.InvalidArgument, "cannot use a closed transaction")
-}
-
-// errUnexpectedTxState returns error for transaction enters an unexpected state.
-func errUnexpectedTxState(ts txState) error {
- return spannerErrorf(codes.FailedPrecondition, "unexpected transaction state: %v", ts)
-}
-
-// ReadOnlyTransaction provides a snapshot transaction with guaranteed
-// consistency across reads, but does not allow writes. Read-only
-// transactions can be configured to read at timestamps in the past.
-//
-// Read-only 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, 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. See the
-// documentation of TimestampBound for more details.
-//
-// A ReadOnlyTransaction consumes resources on the server until Close() is
-// called.
-type ReadOnlyTransaction struct {
- // txReadOnly contains methods for performing transactional reads.
- txReadOnly
-
- // singleUse indicates that the transaction can be used for only one read.
- singleUse bool
-
- // sp is the session pool for allocating a session to execute the read-only transaction. It is set only once during initialization of the ReadOnlyTransaction.
- sp *sessionPool
- // mu protects concurrent access to the internal states of ReadOnlyTransaction.
- mu sync.Mutex
- // tx is the transaction ID in Cloud Spanner that uniquely identifies the ReadOnlyTransaction.
- tx transactionID
- // txReadyOrClosed is for broadcasting that transaction ID has been returned by Cloud Spanner or that transaction is closed.
- txReadyOrClosed chan struct{}
- // state is the current transaction status of the ReadOnly transaction.
- state txState
- // sh is the sessionHandle allocated from sp.
- sh *sessionHandle
- // rts is the read timestamp returned by transactional reads.
- rts time.Time
- // tb is the read staleness bound specification for transactional reads.
- tb TimestampBound
-}
-
-// errTxInitTimeout returns error for timeout in waiting for initialization of the transaction.
-func errTxInitTimeout() error {
- return spannerErrorf(codes.Canceled, "timeout/context canceled in waiting for transaction's initialization")
-}
-
-// getTimestampBound returns the read staleness bound specified for the ReadOnlyTransaction.
-func (t *ReadOnlyTransaction) getTimestampBound() TimestampBound {
- t.mu.Lock()
- defer t.mu.Unlock()
- return t.tb
-}
-
-// begin starts a snapshot read-only Transaction on Cloud Spanner.
-func (t *ReadOnlyTransaction) begin(ctx context.Context) error {
- var (
- locked bool
- tx transactionID
- rts time.Time
- sh *sessionHandle
- err error
- )
- defer func() {
- if !locked {
- t.mu.Lock()
- // Not necessary, just to make it clear that t.mu is being held when locked == true.
- locked = true
- }
- if t.state != txClosed {
- // Signal other initialization routines.
- close(t.txReadyOrClosed)
- t.txReadyOrClosed = make(chan struct{})
- }
- t.mu.Unlock()
- if err != nil && sh != nil {
- // Got a valid session handle, but failed to initalize transaction on Cloud Spanner.
- if shouldDropSession(err) {
- sh.destroy()
- }
- // If sh.destroy was already executed, this becomes a noop.
- sh.recycle()
- }
- }()
- sh, err = t.sp.take(ctx)
- if err != nil {
- return err
- }
- err = runRetryable(contextWithOutgoingMetadata(ctx, sh.getMetadata()), func(ctx context.Context) error {
- res, e := sh.getClient().BeginTransaction(ctx, &sppb.BeginTransactionRequest{
- Session: sh.getID(),
- Options: &sppb.TransactionOptions{
- Mode: &sppb.TransactionOptions_ReadOnly_{
- ReadOnly: buildTransactionOptionsReadOnly(t.getTimestampBound(), true),
- },
- },
- })
- if e != nil {
- return e
- }
- tx = res.Id
- if res.ReadTimestamp != nil {
- rts = time.Unix(res.ReadTimestamp.Seconds, int64(res.ReadTimestamp.Nanos))
- }
- return nil
- })
- t.mu.Lock()
- locked = true // defer function will be executed with t.mu being held.
- if t.state == txClosed { // During the execution of t.begin(), t.Close() was invoked.
- return errSessionClosed(sh)
- }
- // If begin() fails, this allows other queries to take over the initialization.
- t.tx = nil
- if err == nil {
- t.tx = tx
- t.rts = rts
- t.sh = sh
- // State transite to txActive.
- t.state = txActive
- }
- return err
-}
-
-// acquire implements txReadEnv.acquire.
-func (t *ReadOnlyTransaction) acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) {
- if err := checkNestedTxn(ctx); err != nil {
- return nil, nil, err
- }
- if t.singleUse {
- return t.acquireSingleUse(ctx)
- }
- return t.acquireMultiUse(ctx)
-}
-
-func (t *ReadOnlyTransaction) acquireSingleUse(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) {
- t.mu.Lock()
- defer t.mu.Unlock()
- switch t.state {
- case txClosed:
- // A closed single-use transaction can never be reused.
- return nil, nil, errTxClosed()
- case txNew:
- t.state = txClosed
- ts := &sppb.TransactionSelector{
- Selector: &sppb.TransactionSelector_SingleUse{
- SingleUse: &sppb.TransactionOptions{
- Mode: &sppb.TransactionOptions_ReadOnly_{
- ReadOnly: buildTransactionOptionsReadOnly(t.tb, true),
- },
- },
- },
- }
- sh, err := t.sp.take(ctx)
- if err != nil {
- return nil, nil, err
- }
- // Install session handle into t, which can be used for readonly operations later.
- t.sh = sh
- return sh, ts, nil
- }
- us := t.state
- // SingleUse transaction should only be in either txNew state or txClosed state.
- return nil, nil, errUnexpectedTxState(us)
-}
-
-func (t *ReadOnlyTransaction) acquireMultiUse(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) {
- for {
- t.mu.Lock()
- switch t.state {
- case txClosed:
- t.mu.Unlock()
- return nil, nil, errTxClosed()
- case txNew:
- // State transit to txInit so that no further TimestampBound change is accepted.
- t.state = txInit
- t.mu.Unlock()
- continue
- case txInit:
- if t.tx != nil {
- // Wait for a transaction ID to become ready.
- txReadyOrClosed := t.txReadyOrClosed
- t.mu.Unlock()
- select {
- case <-txReadyOrClosed:
- // Need to check transaction state again.
- continue
- case <-ctx.Done():
- // The waiting for initialization is timeout, return error directly.
- return nil, nil, errTxInitTimeout()
- }
- }
- // Take the ownership of initializing the transaction.
- t.tx = transactionID{}
- t.mu.Unlock()
- // Begin a read-only transaction.
- // TODO: consider adding a transaction option which allow queries to initiate transactions by themselves. Note that this option might not be
- // always good because the ID of the new transaction won't be ready till the query returns some data or completes.
- if err := t.begin(ctx); err != nil {
- return nil, nil, err
- }
- // If t.begin() succeeded, t.state should have been changed to txActive, so we can just continue here.
- continue
- case txActive:
- sh := t.sh
- ts := &sppb.TransactionSelector{
- Selector: &sppb.TransactionSelector_Id{
- Id: t.tx,
- },
- }
- t.mu.Unlock()
- return sh, ts, nil
- }
- state := t.state
- t.mu.Unlock()
- return nil, nil, errUnexpectedTxState(state)
- }
-}
-
-func (t *ReadOnlyTransaction) setTimestamp(ts time.Time) {
- t.mu.Lock()
- defer t.mu.Unlock()
- if t.rts.IsZero() {
- t.rts = ts
- }
-}
-
-// release implements txReadEnv.release.
-func (t *ReadOnlyTransaction) release(err error) {
- t.mu.Lock()
- sh := t.sh
- t.mu.Unlock()
- if sh != nil { // sh could be nil if t.acquire() fails.
- if shouldDropSession(err) {
- sh.destroy()
- }
- if t.singleUse {
- // If session handle is already destroyed, this becomes a noop.
- sh.recycle()
- }
- }
-}
-
-// Close closes a ReadOnlyTransaction, the transaction cannot perform any reads after being closed.
-func (t *ReadOnlyTransaction) Close() {
- if t.singleUse {
- return
- }
- t.mu.Lock()
- if t.state != txClosed {
- t.state = txClosed
- close(t.txReadyOrClosed)
- }
- sh := t.sh
- t.mu.Unlock()
- if sh == nil {
- return
- }
- // If session handle is already destroyed, this becomes a noop.
- // If there are still active queries and if the recycled session is reused before they complete, Cloud Spanner will cancel them
- // on behalf of the new transaction on the session.
- if sh != nil {
- sh.recycle()
- }
-}
-
-// Timestamp returns the timestamp chosen to perform reads and
-// queries in this transaction. The value can only be read after some
-// read or query has either returned some data or completed without
-// returning any data.
-func (t *ReadOnlyTransaction) Timestamp() (time.Time, error) {
- t.mu.Lock()
- defer t.mu.Unlock()
- if t.rts.IsZero() {
- return t.rts, errRtsUnavailable()
- }
- return t.rts, nil
-}
-
-// WithTimestampBound specifies the TimestampBound to use for read or query.
-// This can only be used before the first read or query is invoked. Note:
-// bounded staleness is not available with general ReadOnlyTransactions; use a
-// single-use ReadOnlyTransaction instead.
-//
-// The returned value is the ReadOnlyTransaction so calls can be chained.
-func (t *ReadOnlyTransaction) WithTimestampBound(tb TimestampBound) *ReadOnlyTransaction {
- t.mu.Lock()
- defer t.mu.Unlock()
- if t.state == txNew {
- // Only allow to set TimestampBound before the first query.
- t.tb = tb
- }
- return t
-}
-
-// ReadWriteTransaction provides a locking read-write transaction.
-//
-// This type of transaction is the only way to write data into Cloud Spanner;
-// (*Client).Apply and (*Client).ApplyAtLeastOnce use transactions
-// internally. These transactions rely on pessimistic locking and, if
-// necessary, two-phase commit. Locking read-write transactions may abort,
-// requiring the application to retry. However, the interface exposed by
-// (*Client).ReadWriteTransaction eliminates the need for applications to write
-// retry loops explicitly.
-//
-// 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. Long periods of inactivity at the client
-// may cause Cloud Spanner to release a transaction's locks and abort it.
-//
-// Reads performed within a transaction acquire locks on the data being
-// read. Writes can only be done at commit time, after all reads have been
-// completed. Conceptually, a read-write transaction consists of zero or more
-// reads or SQL queries followed by a commit.
-//
-// See (*Client).ReadWriteTransaction for an example.
-//
-// 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.
-//
-// Aborted transactions
-//
-// Application code does not need to retry explicitly; RunInTransaction will
-// automatically retry a transaction if an attempt results in an abort. The
-// lock priority of a transaction increases after each prior aborted
-// transaction, meaning that the next attempt has a slightly better chance of
-// success than before.
-//
-// Under some circumstances (e.g., 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 wall 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. In that case, the commit will fail with error
-// ABORTED.
-//
-// If this behavior is undesirable, periodically executing a simple SQL query
-// in the transaction (e.g., SELECT 1) prevents the transaction from becoming
-// idle.
-type ReadWriteTransaction struct {
- // txReadOnly contains methods for performing transactional reads.
- txReadOnly
- // sh is the sessionHandle allocated from sp. It is set only once during the initialization of ReadWriteTransaction.
- sh *sessionHandle
- // tx is the transaction ID in Cloud Spanner that uniquely identifies the ReadWriteTransaction.
- // It is set only once in ReadWriteTransaction.begin() during the initialization of ReadWriteTransaction.
- tx transactionID
- // mu protects concurrent access to the internal states of ReadWriteTransaction.
- mu sync.Mutex
- // state is the current transaction status of the read-write transaction.
- state txState
- // wb is the set of buffered mutations waiting to be commited.
- wb []*Mutation
-}
-
-// BufferWrite adds a list of mutations to the set of updates that will be
-// applied when the transaction is committed. It does not actually apply the
-// write until the transaction is committed, so the operation does not
-// block. The effects of the write won't be visible to any reads (including
-// reads done in the same transaction) until the transaction commits.
-//
-// See the example for Client.ReadWriteTransaction.
-func (t *ReadWriteTransaction) BufferWrite(ms []*Mutation) error {
- t.mu.Lock()
- defer t.mu.Unlock()
- if t.state == txClosed {
- return errTxClosed()
- }
- if t.state != txActive {
- return errUnexpectedTxState(t.state)
- }
- t.wb = append(t.wb, ms...)
- return nil
-}
-
-// acquire implements txReadEnv.acquire.
-func (t *ReadWriteTransaction) acquire(ctx context.Context) (*sessionHandle, *sppb.TransactionSelector, error) {
- ts := &sppb.TransactionSelector{
- Selector: &sppb.TransactionSelector_Id{
- Id: t.tx,
- },
- }
- t.mu.Lock()
- defer t.mu.Unlock()
- switch t.state {
- case txClosed:
- return nil, nil, errTxClosed()
- case txActive:
- return t.sh, ts, nil
- }
- return nil, nil, errUnexpectedTxState(t.state)
-}
-
-// release implements txReadEnv.release.
-func (t *ReadWriteTransaction) release(err error) {
- t.mu.Lock()
- sh := t.sh
- t.mu.Unlock()
- if sh != nil && shouldDropSession(err) {
- sh.destroy()
- }
-}
-
-func beginTransaction(ctx context.Context, sid string, client sppb.SpannerClient) (transactionID, error) {
- var tx transactionID
- err := runRetryable(ctx, func(ctx context.Context) error {
- res, e := client.BeginTransaction(ctx, &sppb.BeginTransactionRequest{
- Session: sid,
- Options: &sppb.TransactionOptions{
- Mode: &sppb.TransactionOptions_ReadWrite_{
- ReadWrite: &sppb.TransactionOptions_ReadWrite{},
- },
- },
- })
- if e != nil {
- return e
- }
- tx = res.Id
- return nil
- })
- if err != nil {
- return nil, err
- }
- return tx, nil
-}
-
-// begin starts a read-write transacton on Cloud Spanner, it is always called before any of the public APIs.
-func (t *ReadWriteTransaction) begin(ctx context.Context) error {
- if t.tx != nil {
- t.state = txActive
- return nil
- }
- tx, err := beginTransaction(contextWithOutgoingMetadata(ctx, t.sh.getMetadata()), t.sh.getID(), t.sh.getClient())
- if err == nil {
- t.tx = tx
- t.state = txActive
- return nil
- }
- if shouldDropSession(err) {
- t.sh.destroy()
- }
- return err
-}
-
-// commit tries to commit a readwrite transaction to Cloud Spanner. It also returns the commit timestamp for the transactions.
-func (t *ReadWriteTransaction) commit(ctx context.Context) (time.Time, error) {
- var ts time.Time
- t.mu.Lock()
- t.state = txClosed // No futher operations after commit.
- mPb, err := mutationsProto(t.wb)
- t.mu.Unlock()
- if err != nil {
- return ts, err
- }
- // In case that sessionHandle was destroyed but transaction body fails to report it.
- sid, client := t.sh.getID(), t.sh.getClient()
- if sid == "" || client == nil {
- return ts, errSessionClosed(t.sh)
- }
- err = runRetryable(contextWithOutgoingMetadata(ctx, t.sh.getMetadata()), func(ctx context.Context) error {
- var trailer metadata.MD
- res, e := client.Commit(ctx, &sppb.CommitRequest{
- Session: sid,
- Transaction: &sppb.CommitRequest_TransactionId{
- TransactionId: t.tx,
- },
- Mutations: mPb,
- }, grpc.Trailer(&trailer))
- if e != nil {
- return toSpannerErrorWithMetadata(e, trailer)
- }
- if tstamp := res.GetCommitTimestamp(); tstamp != nil {
- ts = time.Unix(tstamp.Seconds, int64(tstamp.Nanos))
- }
- return nil
- })
- if shouldDropSession(err) {
- t.sh.destroy()
- }
- return ts, err
-}
-
-// rollback is called when a commit is aborted or the transaction body runs into error.
-func (t *ReadWriteTransaction) rollback(ctx context.Context) {
- t.mu.Lock()
- // Forbid further operations on rollbacked transaction.
- t.state = txClosed
- t.mu.Unlock()
- // In case that sessionHandle was destroyed but transaction body fails to report it.
- sid, client := t.sh.getID(), t.sh.getClient()
- if sid == "" || client == nil {
- return
- }
- err := runRetryable(contextWithOutgoingMetadata(ctx, t.sh.getMetadata()), func(ctx context.Context) error {
- _, e := client.Rollback(ctx, &sppb.RollbackRequest{
- Session: sid,
- TransactionId: t.tx,
- })
- return e
- })
- if shouldDropSession(err) {
- t.sh.destroy()
- }
- return
-}
-
-// runInTransaction executes f under a read-write transaction context.
-func (t *ReadWriteTransaction) runInTransaction(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error) (time.Time, error) {
- var (
- ts time.Time
- err error
- )
- if err = f(context.WithValue(ctx, transactionInProgressKey{}, 1), t); err == nil {
- // Try to commit if transaction body returns no error.
- ts, err = t.commit(ctx)
- }
- if err != nil {
- if isAbortErr(err) {
- // Retry the transaction using the same session on ABORT error.
- // Cloud Spanner will create the new transaction with the previous one's wound-wait priority.
- err = errRetry(err)
- return ts, err
- }
- // Not going to commit, according to API spec, should rollback the transaction.
- t.rollback(ctx)
- return ts, err
- }
- // err == nil, return commit timestamp.
- return ts, err
-}
-
-// writeOnlyTransaction provides the most efficient way of doing write-only transactions. It essentially does blind writes to Cloud Spanner.
-type writeOnlyTransaction struct {
- // sp is the session pool which writeOnlyTransaction uses to get Cloud Spanner sessions for blind writes.
- sp *sessionPool
-}
-
-// applyAtLeastOnce commits a list of mutations to Cloud Spanner for at least once, unless one of the following happends:
-// 1) Context is timeout.
-// 2) An unretryable error(e.g. database not found) occurs.
-// 3) There is a malformed Mutation object.
-func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Mutation) (time.Time, error) {
- var (
- ts time.Time
- sh *sessionHandle
- )
- mPb, err := mutationsProto(ms)
- if err != nil {
- // Malformed mutation found, just return the error.
- return ts, err
- }
- err = runRetryable(ctx, func(ct context.Context) error {
- var e error
- var trailers metadata.MD
- if sh == nil || sh.getID() == "" || sh.getClient() == nil {
- // No usable session for doing the commit, take one from pool.
- sh, e = t.sp.take(ctx)
- if e != nil {
- // sessionPool.Take already retries for session creations/retrivals.
- return e
- }
- }
- res, e := sh.getClient().Commit(contextWithOutgoingMetadata(ctx, sh.getMetadata()), &sppb.CommitRequest{
- Session: sh.getID(),
- Transaction: &sppb.CommitRequest_SingleUseTransaction{
- SingleUseTransaction: &sppb.TransactionOptions{
- Mode: &sppb.TransactionOptions_ReadWrite_{
- ReadWrite: &sppb.TransactionOptions_ReadWrite{},
- },
- },
- },
- Mutations: mPb,
- }, grpc.Trailer(&trailers))
- if e != nil {
- if isAbortErr(e) {
- // Mask ABORT error as retryable, because aborted transactions are allowed to be retried.
- return errRetry(toSpannerErrorWithMetadata(e, trailers))
- }
- if shouldDropSession(e) {
- // Discard the bad session.
- sh.destroy()
- }
- return e
- }
- if tstamp := res.GetCommitTimestamp(); tstamp != nil {
- ts = time.Unix(tstamp.Seconds, int64(tstamp.Nanos))
- }
- return nil
- })
- if sh != nil {
- sh.recycle()
- }
- return ts, err
-}
-
-// isAbortedErr returns true if the error indicates that an gRPC call is aborted on the server side.
-func isAbortErr(err error) bool {
- if err == nil {
- return false
- }
- if ErrCode(err) == codes.Aborted {
- return true
- }
- return false
-}
diff --git a/vendor/cloud.google.com/go/spanner/value.go b/vendor/cloud.google.com/go/spanner/value.go
deleted file mode 100644
index c09713264..000000000
--- a/vendor/cloud.google.com/go/spanner/value.go
+++ /dev/null
@@ -1,1244 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "encoding/base64"
- "fmt"
- "math"
- "reflect"
- "strconv"
- "strings"
- "time"
-
- "cloud.google.com/go/civil"
- "cloud.google.com/go/internal/fields"
- proto "github.com/golang/protobuf/proto"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
- "google.golang.org/grpc/codes"
-)
-
-// NullInt64 represents a Cloud Spanner INT64 that may be NULL.
-type NullInt64 struct {
- Int64 int64
- Valid bool // Valid is true if Int64 is not NULL.
-}
-
-// String implements Stringer.String for NullInt64
-func (n NullInt64) String() string {
- if !n.Valid {
- return fmt.Sprintf("%v", "<null>")
- }
- return fmt.Sprintf("%v", n.Int64)
-}
-
-// NullString represents a Cloud Spanner STRING that may be NULL.
-type NullString struct {
- StringVal string
- Valid bool // Valid is true if StringVal is not NULL.
-}
-
-// String implements Stringer.String for NullString
-func (n NullString) String() string {
- if !n.Valid {
- return fmt.Sprintf("%v", "<null>")
- }
- return fmt.Sprintf("%q", n.StringVal)
-}
-
-// NullFloat64 represents a Cloud Spanner FLOAT64 that may be NULL.
-type NullFloat64 struct {
- Float64 float64
- Valid bool // Valid is true if Float64 is not NULL.
-}
-
-// String implements Stringer.String for NullFloat64
-func (n NullFloat64) String() string {
- if !n.Valid {
- return fmt.Sprintf("%v", "<null>")
- }
- return fmt.Sprintf("%v", n.Float64)
-}
-
-// NullBool represents a Cloud Spanner BOOL that may be NULL.
-type NullBool struct {
- Bool bool
- Valid bool // Valid is true if Bool is not NULL.
-}
-
-// String implements Stringer.String for NullBool
-func (n NullBool) String() string {
- if !n.Valid {
- return fmt.Sprintf("%v", "<null>")
- }
- return fmt.Sprintf("%v", n.Bool)
-}
-
-// NullTime represents a Cloud Spanner TIMESTAMP that may be null.
-type NullTime struct {
- Time time.Time
- Valid bool // Valid is true if Time is not NULL.
-}
-
-// String implements Stringer.String for NullTime
-func (n NullTime) String() string {
- if !n.Valid {
- return fmt.Sprintf("%s", "<null>")
- }
- return fmt.Sprintf("%q", n.Time.Format(time.RFC3339Nano))
-}
-
-// NullDate represents a Cloud Spanner DATE that may be null.
-type NullDate struct {
- Date civil.Date
- Valid bool // Valid is true if Date is not NULL.
-}
-
-// String implements Stringer.String for NullDate
-func (n NullDate) String() string {
- if !n.Valid {
- return fmt.Sprintf("%s", "<null>")
- }
- return fmt.Sprintf("%q", n.Date)
-}
-
-// NullRow represents a Cloud Spanner STRUCT that may be NULL.
-// See also the document for Row.
-// Note that NullRow is not a valid Cloud Spanner column Type.
-type NullRow struct {
- Row Row
- Valid bool // Valid is true if Row is not NULL.
-}
-
-// GenericColumnValue represents the generic encoded value and type of the
-// column. See google.spanner.v1.ResultSet proto for details. This can be
-// useful for proxying query results when the result types are not known in
-// advance.
-type GenericColumnValue struct {
- Type *sppb.Type
- Value *proto3.Value
-}
-
-// Decode decodes a GenericColumnValue. The ptr argument should be a pointer
-// to a Go value that can accept v.
-func (v GenericColumnValue) Decode(ptr interface{}) error {
- return decodeValue(v.Value, v.Type, ptr)
-}
-
-// NewGenericColumnValue creates a GenericColumnValue from Go value that is
-// valid for Cloud Spanner.
-func newGenericColumnValue(v interface{}) (*GenericColumnValue, error) {
- value, typ, err := encodeValue(v)
- if err != nil {
- return nil, err
- }
- return &GenericColumnValue{Value: value, Type: typ}, nil
-}
-
-// errTypeMismatch returns error for destination not having a compatible type
-// with source Cloud Spanner type.
-func errTypeMismatch(srcType sppb.TypeCode, isArray bool, dst interface{}) error {
- usage := srcType.String()
- if isArray {
- usage = fmt.Sprintf("%v[%v]", sppb.TypeCode_ARRAY, srcType)
- }
- return spannerErrorf(codes.InvalidArgument, "type %T cannot be used for decoding %v", dst, usage)
-}
-
-// errNilSpannerType returns error for nil Cloud Spanner type in decoding.
-func errNilSpannerType() error {
- return spannerErrorf(codes.FailedPrecondition, "unexpected nil Cloud Spanner data type in decoding")
-}
-
-// errNilSrc returns error for decoding from nil proto value.
-func errNilSrc() error {
- return spannerErrorf(codes.FailedPrecondition, "unexpected nil Cloud Spanner value in decoding")
-}
-
-// errNilDst returns error for decoding into nil interface{}.
-func errNilDst(dst interface{}) error {
- return spannerErrorf(codes.InvalidArgument, "cannot decode into nil type %T", dst)
-}
-
-// errNilArrElemType returns error for input Cloud Spanner data type being a array but without a
-// non-nil array element type.
-func errNilArrElemType(t *sppb.Type) error {
- return spannerErrorf(codes.FailedPrecondition, "array type %v is with nil array element type", t)
-}
-
-// errDstNotForNull returns error for decoding a SQL NULL value into a destination which doesn't
-// support NULL values.
-func errDstNotForNull(dst interface{}) error {
- return spannerErrorf(codes.InvalidArgument, "destination %T cannot support NULL SQL values", dst)
-}
-
-// errBadEncoding returns error for decoding wrongly encoded types.
-func errBadEncoding(v *proto3.Value, err error) error {
- return spannerErrorf(codes.FailedPrecondition, "%v wasn't correctly encoded: <%v>", v, err)
-}
-
-func parseNullTime(v *proto3.Value, p *NullTime, code sppb.TypeCode, isNull bool) error {
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_TIMESTAMP {
- return errTypeMismatch(code, false, p)
- }
- if isNull {
- *p = NullTime{}
- return nil
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- y, err := time.Parse(time.RFC3339Nano, x)
- if err != nil {
- return errBadEncoding(v, err)
- }
- p.Valid = true
- p.Time = y
- return nil
-}
-
-// decodeValue decodes a protobuf Value into a pointer to a Go value, as
-// specified by sppb.Type.
-func decodeValue(v *proto3.Value, t *sppb.Type, ptr interface{}) error {
- if v == nil {
- return errNilSrc()
- }
- if t == nil {
- return errNilSpannerType()
- }
- code := t.Code
- acode := sppb.TypeCode_TYPE_CODE_UNSPECIFIED
- if code == sppb.TypeCode_ARRAY {
- if t.ArrayElementType == nil {
- return errNilArrElemType(t)
- }
- acode = t.ArrayElementType.Code
- }
- typeErr := errTypeMismatch(code, false, ptr)
- if code == sppb.TypeCode_ARRAY {
- typeErr = errTypeMismatch(acode, true, ptr)
- }
- nullErr := errDstNotForNull(ptr)
- _, isNull := v.Kind.(*proto3.Value_NullValue)
-
- // Do the decoding based on the type of ptr.
- switch p := ptr.(type) {
- case nil:
- return errNilDst(nil)
- case *string:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_STRING {
- return typeErr
- }
- if isNull {
- return nullErr
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- *p = x
- case *NullString:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_STRING {
- return typeErr
- }
- if isNull {
- *p = NullString{}
- break
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- p.Valid = true
- p.StringVal = x
- case *[]NullString:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_STRING {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeStringArray(x)
- if err != nil {
- return err
- }
- *p = y
- case *[]byte:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_BYTES {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- y, err := base64.StdEncoding.DecodeString(x)
- if err != nil {
- return errBadEncoding(v, err)
- }
- *p = y
- case *[][]byte:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_BYTES {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeByteArray(x)
- if err != nil {
- return err
- }
- *p = y
- case *int64:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_INT64 {
- return typeErr
- }
- if isNull {
- return nullErr
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- y, err := strconv.ParseInt(x, 10, 64)
- if err != nil {
- return errBadEncoding(v, err)
- }
- *p = y
- case *NullInt64:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_INT64 {
- return typeErr
- }
- if isNull {
- *p = NullInt64{}
- break
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- y, err := strconv.ParseInt(x, 10, 64)
- if err != nil {
- return errBadEncoding(v, err)
- }
- p.Valid = true
- p.Int64 = y
- case *[]NullInt64:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_INT64 {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeIntArray(x)
- if err != nil {
- return err
- }
- *p = y
- case *bool:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_BOOL {
- return typeErr
- }
- if isNull {
- return nullErr
- }
- x, err := getBoolValue(v)
- if err != nil {
- return err
- }
- *p = x
- case *NullBool:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_BOOL {
- return typeErr
- }
- if isNull {
- *p = NullBool{}
- break
- }
- x, err := getBoolValue(v)
- if err != nil {
- return err
- }
- p.Valid = true
- p.Bool = x
- case *[]NullBool:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_BOOL {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeBoolArray(x)
- if err != nil {
- return err
- }
- *p = y
- case *float64:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_FLOAT64 {
- return typeErr
- }
- if isNull {
- return nullErr
- }
- x, err := getFloat64Value(v)
- if err != nil {
- return err
- }
- *p = x
- case *NullFloat64:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_FLOAT64 {
- return typeErr
- }
- if isNull {
- *p = NullFloat64{}
- break
- }
- x, err := getFloat64Value(v)
- if err != nil {
- return err
- }
- p.Valid = true
- p.Float64 = x
- case *[]NullFloat64:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_FLOAT64 {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeFloat64Array(x)
- if err != nil {
- return err
- }
- *p = y
- case *time.Time:
- var nt NullTime
- if isNull {
- return nullErr
- }
- err := parseNullTime(v, &nt, code, isNull)
- if err != nil {
- return nil
- }
- *p = nt.Time
- case *NullTime:
- err := parseNullTime(v, p, code, isNull)
- if err != nil {
- return err
- }
- case *[]NullTime:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_TIMESTAMP {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeTimeArray(x)
- if err != nil {
- return err
- }
- *p = y
- case *civil.Date:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_DATE {
- return typeErr
- }
- if isNull {
- return nullErr
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- y, err := civil.ParseDate(x)
- if err != nil {
- return errBadEncoding(v, err)
- }
- *p = y
- case *NullDate:
- if p == nil {
- return errNilDst(p)
- }
- if code != sppb.TypeCode_DATE {
- return typeErr
- }
- if isNull {
- *p = NullDate{}
- break
- }
- x, err := getStringValue(v)
- if err != nil {
- return err
- }
- y, err := civil.ParseDate(x)
- if err != nil {
- return errBadEncoding(v, err)
- }
- p.Valid = true
- p.Date = y
- case *[]NullDate:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_DATE {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeDateArray(x)
- if err != nil {
- return err
- }
- *p = y
- case *[]NullRow:
- if p == nil {
- return errNilDst(p)
- }
- if acode != sppb.TypeCode_STRUCT {
- return typeErr
- }
- if isNull {
- *p = nil
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- y, err := decodeRowArray(t.ArrayElementType.StructType, x)
- if err != nil {
- return err
- }
- *p = y
- case *GenericColumnValue:
- *p = GenericColumnValue{
- // Deep clone to ensure subsequent changes to t or v
- // don't affect our decoded value.
- Type: proto.Clone(t).(*sppb.Type),
- Value: proto.Clone(v).(*proto3.Value),
- }
- default:
- // Check if the proto encoding is for an array of structs.
- if !(code == sppb.TypeCode_ARRAY && acode == sppb.TypeCode_STRUCT) {
- return typeErr
- }
- vp := reflect.ValueOf(p)
- if !vp.IsValid() {
- return errNilDst(p)
- }
- if !isPtrStructPtrSlice(vp.Type()) {
- // The container is not a pointer to a struct pointer slice.
- return typeErr
- }
- // Only use reflection for nil detection on slow path.
- // Also, IsNil panics on many types, so check it after the type check.
- if vp.IsNil() {
- return errNilDst(p)
- }
- if isNull {
- // The proto Value is encoding NULL, set the pointer to struct
- // slice to nil as well.
- vp.Elem().Set(reflect.Zero(vp.Elem().Type()))
- break
- }
- x, err := getListValue(v)
- if err != nil {
- return err
- }
- if err = decodeStructArray(t.ArrayElementType.StructType, x, p); err != nil {
- return err
- }
- }
- return nil
-}
-
-// errSrvVal returns an error for getting a wrong source protobuf value in decoding.
-func errSrcVal(v *proto3.Value, want string) error {
- return spannerErrorf(codes.FailedPrecondition, "cannot use %v(Kind: %T) as %s Value",
- v, v.GetKind(), want)
-}
-
-// getStringValue returns the string value encoded in proto3.Value v whose
-// kind is proto3.Value_StringValue.
-func getStringValue(v *proto3.Value) (string, error) {
- if x, ok := v.GetKind().(*proto3.Value_StringValue); ok && x != nil {
- return x.StringValue, nil
- }
- return "", errSrcVal(v, "String")
-}
-
-// getBoolValue returns the bool value encoded in proto3.Value v whose
-// kind is proto3.Value_BoolValue.
-func getBoolValue(v *proto3.Value) (bool, error) {
- if x, ok := v.GetKind().(*proto3.Value_BoolValue); ok && x != nil {
- return x.BoolValue, nil
- }
- return false, errSrcVal(v, "Bool")
-}
-
-// getListValue returns the proto3.ListValue contained in proto3.Value v whose
-// kind is proto3.Value_ListValue.
-func getListValue(v *proto3.Value) (*proto3.ListValue, error) {
- if x, ok := v.GetKind().(*proto3.Value_ListValue); ok && x != nil {
- return x.ListValue, nil
- }
- return nil, errSrcVal(v, "List")
-}
-
-// errUnexpectedNumStr returns error for decoder getting a unexpected string for
-// representing special float values.
-func errUnexpectedNumStr(s string) error {
- return spannerErrorf(codes.FailedPrecondition, "unexpected string value %q for number", s)
-}
-
-// getFloat64Value returns the float64 value encoded in proto3.Value v whose
-// kind is proto3.Value_NumberValue / proto3.Value_StringValue.
-// Cloud Spanner uses string to encode NaN, Infinity and -Infinity.
-func getFloat64Value(v *proto3.Value) (float64, error) {
- switch x := v.GetKind().(type) {
- case *proto3.Value_NumberValue:
- if x == nil {
- break
- }
- return x.NumberValue, nil
- case *proto3.Value_StringValue:
- if x == nil {
- break
- }
- switch x.StringValue {
- case "NaN":
- return math.NaN(), nil
- case "Infinity":
- return math.Inf(1), nil
- case "-Infinity":
- return math.Inf(-1), nil
- default:
- return 0, errUnexpectedNumStr(x.StringValue)
- }
- }
- return 0, errSrcVal(v, "Number")
-}
-
-// errNilListValue returns error for unexpected nil ListValue in decoding Cloud Spanner ARRAYs.
-func errNilListValue(sqlType string) error {
- return spannerErrorf(codes.FailedPrecondition, "unexpected nil ListValue in decoding %v array", sqlType)
-}
-
-// errDecodeArrayElement returns error for failure in decoding single array element.
-func errDecodeArrayElement(i int, v proto.Message, sqlType string, err error) error {
- se, ok := toSpannerError(err).(*Error)
- if !ok {
- return spannerErrorf(codes.Unknown,
- "cannot decode %v(array element %v) as %v, error = <%v>", v, i, sqlType, err)
- }
- se.decorate(fmt.Sprintf("cannot decode %v(array element %v) as %v", v, i, sqlType))
- return se
-}
-
-// decodeStringArray decodes proto3.ListValue pb into a NullString slice.
-func decodeStringArray(pb *proto3.ListValue) ([]NullString, error) {
- if pb == nil {
- return nil, errNilListValue("STRING")
- }
- a := make([]NullString, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, stringType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "STRING", err)
- }
- }
- return a, nil
-}
-
-// decodeIntArray decodes proto3.ListValue pb into a NullInt64 slice.
-func decodeIntArray(pb *proto3.ListValue) ([]NullInt64, error) {
- if pb == nil {
- return nil, errNilListValue("INT64")
- }
- a := make([]NullInt64, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, intType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "INT64", err)
- }
- }
- return a, nil
-}
-
-// decodeBoolArray decodes proto3.ListValue pb into a NullBool slice.
-func decodeBoolArray(pb *proto3.ListValue) ([]NullBool, error) {
- if pb == nil {
- return nil, errNilListValue("BOOL")
- }
- a := make([]NullBool, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, boolType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "BOOL", err)
- }
- }
- return a, nil
-}
-
-// decodeFloat64Array decodes proto3.ListValue pb into a NullFloat64 slice.
-func decodeFloat64Array(pb *proto3.ListValue) ([]NullFloat64, error) {
- if pb == nil {
- return nil, errNilListValue("FLOAT64")
- }
- a := make([]NullFloat64, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, floatType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "FLOAT64", err)
- }
- }
- return a, nil
-}
-
-// decodeByteArray decodes proto3.ListValue pb into a slice of byte slice.
-func decodeByteArray(pb *proto3.ListValue) ([][]byte, error) {
- if pb == nil {
- return nil, errNilListValue("BYTES")
- }
- a := make([][]byte, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, bytesType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "BYTES", err)
- }
- }
- return a, nil
-}
-
-// decodeTimeArray decodes proto3.ListValue pb into a NullTime slice.
-func decodeTimeArray(pb *proto3.ListValue) ([]NullTime, error) {
- if pb == nil {
- return nil, errNilListValue("TIMESTAMP")
- }
- a := make([]NullTime, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, timeType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "TIMESTAMP", err)
- }
- }
- return a, nil
-}
-
-// decodeDateArray decodes proto3.ListValue pb into a NullDate slice.
-func decodeDateArray(pb *proto3.ListValue) ([]NullDate, error) {
- if pb == nil {
- return nil, errNilListValue("DATE")
- }
- a := make([]NullDate, len(pb.Values))
- for i, v := range pb.Values {
- if err := decodeValue(v, dateType(), &a[i]); err != nil {
- return nil, errDecodeArrayElement(i, v, "DATE", err)
- }
- }
- return a, nil
-}
-
-func errNotStructElement(i int, v *proto3.Value) error {
- return errDecodeArrayElement(i, v, "STRUCT",
- spannerErrorf(codes.FailedPrecondition, "%v(type: %T) doesn't encode Cloud Spanner STRUCT", v, v))
-}
-
-// decodeRowArray decodes proto3.ListValue pb into a NullRow slice according to
-// the structual information given in sppb.StructType ty.
-func decodeRowArray(ty *sppb.StructType, pb *proto3.ListValue) ([]NullRow, error) {
- if pb == nil {
- return nil, errNilListValue("STRUCT")
- }
- a := make([]NullRow, len(pb.Values))
- for i := range pb.Values {
- switch v := pb.Values[i].GetKind().(type) {
- case *proto3.Value_ListValue:
- a[i] = NullRow{
- Row: Row{
- fields: ty.Fields,
- vals: v.ListValue.Values,
- },
- Valid: true,
- }
- // Null elements not currently supported by the server, see
- // https://cloud.google.com/spanner/docs/query-syntax#using-structs-with-select
- case *proto3.Value_NullValue:
- // no-op, a[i] is NullRow{} already
- default:
- return nil, errNotStructElement(i, pb.Values[i])
- }
- }
- return a, nil
-}
-
-// structFieldColumn returns the name of i-th field of struct type typ if the field
-// is untagged; otherwise, it returns the tagged name of the field.
-func structFieldColumn(typ reflect.Type, i int) (col string, ok bool) {
- desc := typ.Field(i)
- if desc.PkgPath != "" || desc.Anonymous {
- // Skip unexported or anonymous fields.
- return "", false
- }
- col = desc.Name
- if tag := desc.Tag.Get("spanner"); tag != "" {
- if tag == "-" {
- // Skip fields tagged "-" to match encoding/json and others.
- return "", false
- }
- col = tag
- if idx := strings.Index(tag, ","); idx != -1 {
- col = tag[:idx]
- }
- }
- return col, true
-}
-
-// errNilSpannerStructType returns error for unexpected nil Cloud Spanner STRUCT schema type in decoding.
-func errNilSpannerStructType() error {
- return spannerErrorf(codes.FailedPrecondition, "unexpected nil StructType in decoding Cloud Spanner STRUCT")
-}
-
-// errUnnamedField returns error for decoding a Cloud Spanner STRUCT with unnamed field into a Go struct.
-func errUnnamedField(ty *sppb.StructType, i int) error {
- return spannerErrorf(codes.InvalidArgument, "unnamed field %v in Cloud Spanner STRUCT %+v", i, ty)
-}
-
-// errNoOrDupGoField returns error for decoding a Cloud Spanner
-// STRUCT into a Go struct which is either missing a field, or has duplicate fields.
-func errNoOrDupGoField(s interface{}, f string) error {
- return spannerErrorf(codes.InvalidArgument, "Go struct %+v(type %T) has no or duplicate fields for Cloud Spanner STRUCT field %v", s, s, f)
-}
-
-// errDupColNames returns error for duplicated Cloud Spanner STRUCT field names found in decoding a Cloud Spanner STRUCT into a Go struct.
-func errDupSpannerField(f string, ty *sppb.StructType) error {
- return spannerErrorf(codes.InvalidArgument, "duplicated field name %q in Cloud Spanner STRUCT %+v", f, ty)
-}
-
-// errDecodeStructField returns error for failure in decoding a single field of a Cloud Spanner STRUCT.
-func errDecodeStructField(ty *sppb.StructType, f string, err error) error {
- se, ok := toSpannerError(err).(*Error)
- if !ok {
- return spannerErrorf(codes.Unknown,
- "cannot decode field %v of Cloud Spanner STRUCT %+v, error = <%v>", f, ty, err)
- }
- se.decorate(fmt.Sprintf("cannot decode field %v of Cloud Spanner STRUCT %+v", f, ty))
- return se
-}
-
-// decodeStruct decodes proto3.ListValue pb into struct referenced by pointer ptr, according to
-// the structual information given in sppb.StructType ty.
-func decodeStruct(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error {
- if reflect.ValueOf(ptr).IsNil() {
- return errNilDst(ptr)
- }
- if ty == nil {
- return errNilSpannerStructType()
- }
- // t holds the structual information of ptr.
- t := reflect.TypeOf(ptr).Elem()
- // v is the actual value that ptr points to.
- v := reflect.ValueOf(ptr).Elem()
-
- fields, err := fieldCache.Fields(t)
- if err != nil {
- return toSpannerError(err)
- }
- seen := map[string]bool{}
- for i, f := range ty.Fields {
- if f.Name == "" {
- return errUnnamedField(ty, i)
- }
- sf := fields.Match(f.Name)
- if sf == nil {
- return errNoOrDupGoField(ptr, f.Name)
- }
- if seen[f.Name] {
- // We don't allow duplicated field name.
- return errDupSpannerField(f.Name, ty)
- }
- // Try to decode a single field.
- if err := decodeValue(pb.Values[i], f.Type, v.FieldByIndex(sf.Index).Addr().Interface()); err != nil {
- return errDecodeStructField(ty, f.Name, err)
- }
- // Mark field f.Name as processed.
- seen[f.Name] = true
- }
- return nil
-}
-
-// isPtrStructPtrSlice returns true if ptr is a pointer to a slice of struct pointers.
-func isPtrStructPtrSlice(t reflect.Type) bool {
- if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
- // t is not a pointer to a slice.
- return false
- }
- if t = t.Elem(); t.Elem().Kind() != reflect.Ptr || t.Elem().Elem().Kind() != reflect.Struct {
- // the slice that t points to is not a slice of struct pointers.
- return false
- }
- return true
-}
-
-// decodeStructArray decodes proto3.ListValue pb into struct slice referenced by pointer ptr, according to the
-// structual information given in a sppb.StructType.
-func decodeStructArray(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) error {
- if pb == nil {
- return errNilListValue("STRUCT")
- }
- // Type of the struct pointers stored in the slice that ptr points to.
- ts := reflect.TypeOf(ptr).Elem().Elem()
- // The slice that ptr points to, might be nil at this point.
- v := reflect.ValueOf(ptr).Elem()
- // Allocate empty slice.
- v.Set(reflect.MakeSlice(v.Type(), 0, len(pb.Values)))
- // Decode every struct in pb.Values.
- for i, pv := range pb.Values {
- // Check if pv is a NULL value.
- if _, isNull := pv.Kind.(*proto3.Value_NullValue); isNull {
- // Append a nil pointer to the slice.
- v.Set(reflect.Append(v, reflect.New(ts).Elem()))
- continue
- }
- // Allocate empty struct.
- s := reflect.New(ts.Elem())
- // Get proto3.ListValue l from proto3.Value pv.
- l, err := getListValue(pv)
- if err != nil {
- return errDecodeArrayElement(i, pv, "STRUCT", err)
- }
- // Decode proto3.ListValue l into struct referenced by s.Interface().
- if err = decodeStruct(ty, l, s.Interface()); err != nil {
- return errDecodeArrayElement(i, pv, "STRUCT", err)
- }
- // Append the decoded struct back into the slice.
- v.Set(reflect.Append(v, s))
- }
- return nil
-}
-
-// errEncoderUnsupportedType returns error for not being able to encode a value of
-// certain type.
-func errEncoderUnsupportedType(v interface{}) error {
- return spannerErrorf(codes.InvalidArgument, "client doesn't support type %T", v)
-}
-
-// encodeValue encodes a Go native type into a proto3.Value.
-func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
- pb := &proto3.Value{
- Kind: &proto3.Value_NullValue{NullValue: proto3.NullValue_NULL_VALUE},
- }
- var pt *sppb.Type
- var err error
- switch v := v.(type) {
- case nil:
- case string:
- pb.Kind = stringKind(v)
- pt = stringType()
- case NullString:
- if v.Valid {
- return encodeValue(v.StringVal)
- }
- case []string:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(stringType())
- }
- case []NullString:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(stringType())
- }
- case []byte:
- if v != nil {
- pb.Kind = stringKind(base64.StdEncoding.EncodeToString(v))
- pt = bytesType()
- }
- case [][]byte:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(bytesType())
- }
- case int:
- pb.Kind = stringKind(strconv.FormatInt(int64(v), 10))
- pt = intType()
- case []int:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(intType())
- }
- case int64:
- pb.Kind = stringKind(strconv.FormatInt(v, 10))
- pt = intType()
- case []int64:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(intType())
- }
- case NullInt64:
- if v.Valid {
- return encodeValue(v.Int64)
- }
- case []NullInt64:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(intType())
- }
- case bool:
- pb.Kind = &proto3.Value_BoolValue{BoolValue: v}
- pt = boolType()
- case []bool:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(boolType())
- }
- case NullBool:
- if v.Valid {
- return encodeValue(v.Bool)
- }
- case []NullBool:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(boolType())
- }
- case float64:
- pb.Kind = &proto3.Value_NumberValue{NumberValue: v}
- pt = floatType()
- case []float64:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(floatType())
- }
- case NullFloat64:
- if v.Valid {
- return encodeValue(v.Float64)
- }
- case []NullFloat64:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(floatType())
- }
- case time.Time:
- pb.Kind = stringKind(v.UTC().Format(time.RFC3339Nano))
- pt = timeType()
- case []time.Time:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(timeType())
- }
- case NullTime:
- if v.Valid {
- return encodeValue(v.Time)
- }
- case []NullTime:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(timeType())
- }
- case civil.Date:
- pb.Kind = stringKind(v.String())
- pt = dateType()
- case []civil.Date:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(dateType())
- }
- case NullDate:
- if v.Valid {
- return encodeValue(v.Date)
- }
- case []NullDate:
- if v != nil {
- pb, err = encodeArray(len(v), func(i int) interface{} { return v[i] })
- if err != nil {
- return nil, nil, err
- }
- pt = listType(dateType())
- }
- case GenericColumnValue:
- // Deep clone to ensure subsequent changes to v before
- // transmission don't affect our encoded value.
- pb = proto.Clone(v.Value).(*proto3.Value)
- pt = proto.Clone(v.Type).(*sppb.Type)
- default:
- return nil, nil, errEncoderUnsupportedType(v)
- }
- return pb, pt, nil
-}
-
-// encodeValueArray encodes a Value array into a proto3.ListValue.
-func encodeValueArray(vs []interface{}) (*proto3.ListValue, error) {
- lv := &proto3.ListValue{}
- lv.Values = make([]*proto3.Value, 0, len(vs))
- for _, v := range vs {
- pb, _, err := encodeValue(v)
- if err != nil {
- return nil, err
- }
- lv.Values = append(lv.Values, pb)
- }
- return lv, nil
-}
-
-// encodeArray assumes that all values of the array element type encode without error.
-func encodeArray(len int, at func(int) interface{}) (*proto3.Value, error) {
- vs := make([]*proto3.Value, len)
- var err error
- for i := 0; i < len; i++ {
- vs[i], _, err = encodeValue(at(i))
- if err != nil {
- return nil, err
- }
- }
- return listProto(vs...), nil
-}
-
-func spannerTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) {
- if s := t.Get("spanner"); s != "" {
- if s == "-" {
- return "", false, nil, nil
- }
- return s, true, nil, nil
- }
- return "", true, nil, nil
-}
-
-var fieldCache = fields.NewCache(spannerTagParser, nil, nil)
diff --git a/vendor/cloud.google.com/go/spanner/value_test.go b/vendor/cloud.google.com/go/spanner/value_test.go
deleted file mode 100644
index 00748b0a1..000000000
--- a/vendor/cloud.google.com/go/spanner/value_test.go
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
-Copyright 2017 Google Inc. All Rights Reserved.
-
-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.
-*/
-
-package spanner
-
-import (
- "math"
- "reflect"
- "testing"
- "time"
-
- "cloud.google.com/go/civil"
- "github.com/golang/protobuf/proto"
- proto3 "github.com/golang/protobuf/ptypes/struct"
- sppb "google.golang.org/genproto/googleapis/spanner/v1"
-)
-
-var (
- t1 = mustParseTime("2016-11-15T15:04:05.999999999Z")
- // Boundaries
- t2 = mustParseTime("0000-01-01T00:00:00.000000000Z")
- t3 = mustParseTime("9999-12-31T23:59:59.999999999Z")
- // Local timezone
- t4 = time.Now()
- d1 = mustParseDate("2016-11-15")
- d2 = mustParseDate("1678-01-01")
-)
-
-func mustParseTime(s string) time.Time {
- t, err := time.Parse(time.RFC3339Nano, s)
- if err != nil {
- panic(err)
- }
- return t
-}
-
-func mustParseDate(s string) civil.Date {
- d, err := civil.ParseDate(s)
- if err != nil {
- panic(err)
- }
- return d
-}
-
-// Test encoding Values.
-func TestEncodeValue(t *testing.T) {
- var (
- tString = stringType()
- tInt = intType()
- tBool = boolType()
- tFloat = floatType()
- tBytes = bytesType()
- tTime = timeType()
- tDate = dateType()
- )
- for i, test := range []struct {
- in interface{}
- want *proto3.Value
- wantType *sppb.Type
- }{
- // STRING / STRING ARRAY
- {"abc", stringProto("abc"), tString},
- {NullString{"abc", true}, stringProto("abc"), tString},
- {NullString{"abc", false}, nullProto(), nil},
- {[]string{"abc", "bcd"}, listProto(stringProto("abc"), stringProto("bcd")), listType(tString)},
- {[]NullString{{"abcd", true}, {"xyz", false}}, listProto(stringProto("abcd"), nullProto()), listType(tString)},
- // BYTES / BYTES ARRAY
- {[]byte("foo"), bytesProto([]byte("foo")), tBytes},
- {[]byte(nil), nullProto(), nil},
- {[][]byte{nil, []byte("ab")}, listProto(nullProto(), bytesProto([]byte("ab"))), listType(tBytes)},
- {[][]byte(nil), nullProto(), nil},
- // INT64 / INT64 ARRAY
- {7, intProto(7), tInt},
- {[]int{31, 127}, listProto(intProto(31), intProto(127)), listType(tInt)},
- {int64(81), intProto(81), tInt},
- {[]int64{33, 129}, listProto(intProto(33), intProto(129)), listType(tInt)},
- {NullInt64{11, true}, intProto(11), tInt},
- {NullInt64{11, false}, nullProto(), nil},
- {[]NullInt64{{35, true}, {131, false}}, listProto(intProto(35), nullProto()), listType(tInt)},
- // BOOL / BOOL ARRAY
- {true, boolProto(true), tBool},
- {NullBool{true, true}, boolProto(true), tBool},
- {NullBool{true, false}, nullProto(), nil},
- {[]bool{true, false}, listProto(boolProto(true), boolProto(false)), listType(tBool)},
- {[]NullBool{{true, true}, {true, false}}, listProto(boolProto(true), nullProto()), listType(tBool)},
- // FLOAT64 / FLOAT64 ARRAY
- {3.14, floatProto(3.14), tFloat},
- {NullFloat64{3.1415, true}, floatProto(3.1415), tFloat},
- {NullFloat64{math.Inf(1), true}, floatProto(math.Inf(1)), tFloat},
- {NullFloat64{3.14159, false}, nullProto(), nil},
- {[]float64{3.141, 0.618, math.Inf(-1)}, listProto(floatProto(3.141), floatProto(0.618), floatProto(math.Inf(-1))), listType(tFloat)},
- {[]NullFloat64{{3.141, true}, {0.618, false}}, listProto(floatProto(3.141), nullProto()), listType(tFloat)},
- // TIMESTAMP / TIMESTAMP ARRAY
- {t1, timeProto(t1), tTime},
- {NullTime{t1, true}, timeProto(t1), tTime},
- {NullTime{t1, false}, nullProto(), nil},
- {[]time.Time{t1, t2, t3, t4}, listProto(timeProto(t1), timeProto(t2), timeProto(t3), timeProto(t4)), listType(tTime)},
- {[]NullTime{{t1, true}, {t1, false}}, listProto(timeProto(t1), nullProto()), listType(tTime)},
- // DATE / DATE ARRAY
- {d1, dateProto(d1), tDate},
- {NullDate{d1, true}, dateProto(d1), tDate},
- {NullDate{civil.Date{}, false}, nullProto(), nil},
- {[]civil.Date{d1, d2}, listProto(dateProto(d1), dateProto(d2)), listType(tDate)},
- {[]NullDate{{d1, true}, {civil.Date{}, false}}, listProto(dateProto(d1), nullProto()), listType(tDate)},
- // GenericColumnValue
- {GenericColumnValue{tString, stringProto("abc")}, stringProto("abc"), tString},
- {GenericColumnValue{tString, nullProto()}, nullProto(), tString},
- // not actually valid (stringProto inside int list), but demonstrates pass-through.
- {
- GenericColumnValue{
- Type: listType(tInt),
- Value: listProto(intProto(5), nullProto(), stringProto("bcd")),
- },
- listProto(intProto(5), nullProto(), stringProto("bcd")),
- listType(tInt),
- },
- } {
- got, gotType, err := encodeValue(test.in)
- if err != nil {
- t.Fatalf("#%d: got error during encoding: %v, want nil", i, err)
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("#%d: got encode result: %v, want %v", i, got, test.want)
- }
- if !reflect.DeepEqual(gotType, test.wantType) {
- t.Errorf("#%d: got encode type: %v, want %v", i, gotType, test.wantType)
- }
- }
-}
-
-// Test decoding Values.
-func TestDecodeValue(t *testing.T) {
- for i, test := range []struct {
- in *proto3.Value
- t *sppb.Type
- want interface{}
- fail bool
- }{
- // STRING
- {stringProto("abc"), stringType(), "abc", false},
- {nullProto(), stringType(), "abc", true},
- {stringProto("abc"), stringType(), NullString{"abc", true}, false},
- {nullProto(), stringType(), NullString{}, false},
- // STRING ARRAY
- {
- listProto(stringProto("abc"), nullProto(), stringProto("bcd")),
- listType(stringType()),
- []NullString{{"abc", true}, {}, {"bcd", true}},
- false,
- },
- {nullProto(), listType(stringType()), []NullString(nil), false},
- // BYTES
- {bytesProto([]byte("ab")), bytesType(), []byte("ab"), false},
- {nullProto(), bytesType(), []byte(nil), false},
- // BYTES ARRAY
- {listProto(bytesProto([]byte("ab")), nullProto()), listType(bytesType()), [][]byte{[]byte("ab"), nil}, false},
- {nullProto(), listType(bytesType()), [][]byte(nil), false},
- //INT64
- {intProto(15), intType(), int64(15), false},
- {nullProto(), intType(), int64(0), true},
- {intProto(15), intType(), NullInt64{15, true}, false},
- {nullProto(), intType(), NullInt64{}, false},
- // INT64 ARRAY
- {listProto(intProto(91), nullProto(), intProto(87)), listType(intType()), []NullInt64{{91, true}, {}, {87, true}}, false},
- {nullProto(), listType(intType()), []NullInt64(nil), false},
- // BOOL
- {boolProto(true), boolType(), true, false},
- {nullProto(), boolType(), true, true},
- {boolProto(true), boolType(), NullBool{true, true}, false},
- {nullProto(), boolType(), NullBool{}, false},
- // BOOL ARRAY
- {listProto(boolProto(true), boolProto(false), nullProto()), listType(boolType()), []NullBool{{true, true}, {false, true}, {}}, false},
- {nullProto(), listType(boolType()), []NullBool(nil), false},
- // FLOAT64
- {floatProto(3.14), floatType(), 3.14, false},
- {nullProto(), floatType(), 0.00, true},
- {floatProto(3.14), floatType(), NullFloat64{3.14, true}, false},
- {nullProto(), floatType(), NullFloat64{}, false},
- // FLOAT64 ARRAY
- {
- listProto(floatProto(math.Inf(1)), floatProto(math.Inf(-1)), nullProto(), floatProto(3.1)),
- listType(floatType()),
- []NullFloat64{{math.Inf(1), true}, {math.Inf(-1), true}, {}, {3.1, true}},
- false,
- },
- {nullProto(), listType(floatType()), []NullFloat64(nil), false},
- // TIMESTAMP
- {timeProto(t1), timeType(), t1, false},
- {timeProto(t1), timeType(), NullTime{t1, true}, false},
- {nullProto(), timeType(), NullTime{}, false},
- // TIMESTAMP ARRAY
- {listProto(timeProto(t1), timeProto(t2), timeProto(t3), nullProto()), listType(timeType()), []NullTime{{t1, true}, {t2, true}, {t3, true}, {}}, false},
- {nullProto(), listType(timeType()), []NullTime(nil), false},
- // DATE
- {dateProto(d1), dateType(), d1, false},
- {dateProto(d1), dateType(), NullDate{d1, true}, false},
- {nullProto(), dateType(), NullDate{}, false},
- // DATE ARRAY
- {listProto(dateProto(d1), dateProto(d2), nullProto()), listType(dateType()), []NullDate{{d1, true}, {d2, true}, {}}, false},
- {nullProto(), listType(dateType()), []NullDate(nil), false},
- // STRUCT ARRAY
- // STRUCT schema is equal to the following Go struct:
- // type s struct {
- // Col1 NullInt64
- // Col2 []struct {
- // SubCol1 float64
- // SubCol2 string
- // }
- // }
- {
- in: listProto(
- listProto(
- intProto(3),
- listProto(
- listProto(floatProto(3.14), stringProto("this")),
- listProto(floatProto(0.57), stringProto("siht")),
- ),
- ),
- listProto(
- nullProto(),
- nullProto(),
- ),
- nullProto(),
- ),
- t: listType(
- structType(
- mkField("Col1", intType()),
- mkField(
- "Col2",
- listType(
- structType(
- mkField("SubCol1", floatType()),
- mkField("SubCol2", stringType()),
- ),
- ),
- ),
- ),
- ),
- want: []NullRow{
- {
- Row: Row{
- fields: []*sppb.StructType_Field{
- mkField("Col1", intType()),
- mkField(
- "Col2",
- listType(
- structType(
- mkField("SubCol1", floatType()),
- mkField("SubCol2", stringType()),
- ),
- ),
- ),
- },
- vals: []*proto3.Value{
- intProto(3),
- listProto(
- listProto(floatProto(3.14), stringProto("this")),
- listProto(floatProto(0.57), stringProto("siht")),
- ),
- },
- },
- Valid: true,
- },
- {
- Row: Row{
- fields: []*sppb.StructType_Field{
- mkField("Col1", intType()),
- mkField(
- "Col2",
- listType(
- structType(
- mkField("SubCol1", floatType()),
- mkField("SubCol2", stringType()),
- ),
- ),
- ),
- },
- vals: []*proto3.Value{
- nullProto(),
- nullProto(),
- },
- },
- Valid: true,
- },
- {},
- },
- fail: false,
- },
- {
- in: listProto(
- listProto(
- intProto(3),
- listProto(
- listProto(floatProto(3.14), stringProto("this")),
- listProto(floatProto(0.57), stringProto("siht")),
- ),
- ),
- listProto(
- nullProto(),
- nullProto(),
- ),
- nullProto(),
- ),
- t: listType(
- structType(
- mkField("Col1", intType()),
- mkField(
- "Col2",
- listType(
- structType(
- mkField("SubCol1", floatType()),
- mkField("SubCol2", stringType()),
- ),
- ),
- ),
- ),
- ),
- want: []*struct {
- Col1 NullInt64
- StructCol []*struct {
- SubCol1 NullFloat64
- SubCol2 string
- } `spanner:"Col2"`
- }{
- {
- Col1: NullInt64{3, true},
- StructCol: []*struct {
- SubCol1 NullFloat64
- SubCol2 string
- }{
- {
- SubCol1: NullFloat64{3.14, true},
- SubCol2: "this",
- },
- {
- SubCol1: NullFloat64{0.57, true},
- SubCol2: "siht",
- },
- },
- },
- {
- Col1: NullInt64{},
- StructCol: []*struct {
- SubCol1 NullFloat64
- SubCol2 string
- }(nil),
- },
- nil,
- },
- fail: false,
- },
- // GenericColumnValue
- {stringProto("abc"), stringType(), GenericColumnValue{stringType(), stringProto("abc")}, false},
- {nullProto(), stringType(), GenericColumnValue{stringType(), nullProto()}, false},
- // not actually valid (stringProto inside int list), but demonstrates pass-through.
- {
- in: listProto(intProto(5), nullProto(), stringProto("bcd")),
- t: listType(intType()),
- want: GenericColumnValue{
- Type: listType(intType()),
- Value: listProto(intProto(5), nullProto(), stringProto("bcd")),
- },
- fail: false,
- },
- } {
- gotp := reflect.New(reflect.TypeOf(test.want))
- if err := decodeValue(test.in, test.t, gotp.Interface()); err != nil {
- if !test.fail {
- t.Errorf("%d: cannot decode %v(%v): %v", i, test.in, test.t, err)
- }
- continue
- }
- if test.fail {
- t.Errorf("%d: decoding %v(%v) succeeds unexpectedly, want error", i, test.in, test.t)
- continue
- }
- got := reflect.Indirect(gotp).Interface()
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("%d: unexpected decoding result - got %v, want %v", i, got, test.want)
- continue
- }
- }
-}
-
-// Test error cases for decodeValue.
-func TestDecodeValueErrors(t *testing.T) {
- for i, test := range []struct {
- in *proto3.Value
- t *sppb.Type
- v interface{}
- }{
- {nullProto(), stringType(), nil},
- {nullProto(), stringType(), 1},
- } {
- err := decodeValue(test.in, test.t, test.v)
- if err == nil {
- t.Errorf("#%d: want error, got nil", i)
- }
- }
-}
-
-// Test NaN encoding/decoding.
-func TestNaN(t *testing.T) {
- // Decode NaN value.
- f := 0.0
- nf := NullFloat64{}
- // To float64
- if err := decodeValue(floatProto(math.NaN()), floatType(), &f); err != nil {
- t.Errorf("decodeValue returns %q for %v, want nil", err, floatProto(math.NaN()))
- }
- if !math.IsNaN(f) {
- t.Errorf("f = %v, want %v", f, math.NaN())
- }
- // To NullFloat64
- if err := decodeValue(floatProto(math.NaN()), floatType(), &nf); err != nil {
- t.Errorf("decodeValue returns %q for %v, want nil", err, floatProto(math.NaN()))
- }
- if !math.IsNaN(nf.Float64) || !nf.Valid {
- t.Errorf("f = %v, want %v", f, NullFloat64{math.NaN(), true})
- }
- // Encode NaN value
- // From float64
- v, _, err := encodeValue(math.NaN())
- if err != nil {
- t.Errorf("encodeValue returns %q for NaN, want nil", err)
- }
- x, ok := v.GetKind().(*proto3.Value_NumberValue)
- if !ok {
- t.Errorf("incorrect type for v.GetKind(): %T, want *proto3.Value_NumberValue", v.GetKind())
- }
- if !math.IsNaN(x.NumberValue) {
- t.Errorf("x.NumberValue = %v, want %v", x.NumberValue, math.NaN())
- }
- // From NullFloat64
- v, _, err = encodeValue(NullFloat64{math.NaN(), true})
- if err != nil {
- t.Errorf("encodeValue returns %q for NaN, want nil", err)
- }
- x, ok = v.GetKind().(*proto3.Value_NumberValue)
- if !ok {
- t.Errorf("incorrect type for v.GetKind(): %T, want *proto3.Value_NumberValue", v.GetKind())
- }
- if !math.IsNaN(x.NumberValue) {
- t.Errorf("x.NumberValue = %v, want %v", x.NumberValue, math.NaN())
- }
-}
-
-func TestGenericColumnValue(t *testing.T) {
- for _, test := range []struct {
- in GenericColumnValue
- want interface{}
- fail bool
- }{
- {GenericColumnValue{stringType(), stringProto("abc")}, "abc", false},
- {GenericColumnValue{stringType(), stringProto("abc")}, 5, true},
- {GenericColumnValue{listType(intType()), listProto(intProto(91), nullProto(), intProto(87))}, []NullInt64{{91, true}, {}, {87, true}}, false},
- {GenericColumnValue{intType(), intProto(42)}, GenericColumnValue{intType(), intProto(42)}, false}, // trippy! :-)
- } {
- // We take a copy and mutate because we're paranoid about immutability.
- inCopy := GenericColumnValue{
- Type: proto.Clone(test.in.Type).(*sppb.Type),
- Value: proto.Clone(test.in.Value).(*proto3.Value),
- }
- gotp := reflect.New(reflect.TypeOf(test.want))
- if err := inCopy.Decode(gotp.Interface()); err != nil {
- if !test.fail {
- t.Errorf("cannot decode %v to %v: %v", test.in, test.want, err)
- }
- continue
- }
- if test.fail {
- t.Errorf("decoding %v to %v succeeds unexpectedly", test.in, test.want)
- }
- // mutations to inCopy should be invisible to gotp.
- inCopy.Type.Code = sppb.TypeCode_TIMESTAMP
- inCopy.Value.Kind = &proto3.Value_NumberValue{NumberValue: 999}
- got := reflect.Indirect(gotp).Interface()
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("unexpected decode result - got %v, want %v", got, test.want)
- }
-
- // Test we can go backwards as well.
- v, err := newGenericColumnValue(test.want)
- if err != nil {
- t.Errorf("NewGenericColumnValue failed: %v", err)
- continue
- }
- if !reflect.DeepEqual(*v, test.in) {
- t.Errorf("unexpected encode result - got %v, want %v", v, test.in)
- }
- // If want is a GenericColumnValue, mutate its underlying value to validate
- // we have taken a deep copy.
- if gcv, ok := test.want.(GenericColumnValue); ok {
- gcv.Type.Code = sppb.TypeCode_TIMESTAMP
- gcv.Value.Kind = &proto3.Value_NumberValue{NumberValue: 999}
- if !reflect.DeepEqual(*v, test.in) {
- t.Errorf("expected deep copy - got %v, want %v", v, test.in)
- }
- }
- }
-}
-
-func runBench(b *testing.B, size int, f func(a []int) (*proto3.Value, *sppb.Type, error)) {
- a := make([]int, size)
- for i := 0; i < b.N; i++ {
- f(a)
- }
-}
-
-func BenchmarkEncodeIntArrayOrig1(b *testing.B) {
- runBench(b, 1, encodeIntArrayOrig)
-}
-
-func BenchmarkEncodeIntArrayOrig10(b *testing.B) {
- runBench(b, 10, encodeIntArrayOrig)
-}
-
-func BenchmarkEncodeIntArrayOrig100(b *testing.B) {
- runBench(b, 100, encodeIntArrayOrig)
-}
-
-func BenchmarkEncodeIntArrayOrig1000(b *testing.B) {
- runBench(b, 1000, encodeIntArrayOrig)
-}
-
-func BenchmarkEncodeIntArrayFunc1(b *testing.B) {
- runBench(b, 1, encodeIntArrayFunc)
-}
-
-func BenchmarkEncodeIntArrayFunc10(b *testing.B) {
- runBench(b, 10, encodeIntArrayFunc)
-}
-
-func BenchmarkEncodeIntArrayFunc100(b *testing.B) {
- runBench(b, 100, encodeIntArrayFunc)
-}
-
-func BenchmarkEncodeIntArrayFunc1000(b *testing.B) {
- runBench(b, 1000, encodeIntArrayFunc)
-}
-
-func BenchmarkEncodeIntArrayReflect1(b *testing.B) {
- runBench(b, 1, encodeIntArrayReflect)
-}
-
-func BenchmarkEncodeIntArrayReflect10(b *testing.B) {
- runBench(b, 10, encodeIntArrayReflect)
-}
-
-func BenchmarkEncodeIntArrayReflect100(b *testing.B) {
- runBench(b, 100, encodeIntArrayReflect)
-}
-
-func BenchmarkEncodeIntArrayReflect1000(b *testing.B) {
- runBench(b, 1000, encodeIntArrayReflect)
-}
-
-func encodeIntArrayOrig(a []int) (*proto3.Value, *sppb.Type, error) {
- vs := make([]*proto3.Value, len(a))
- var err error
- for i := range a {
- vs[i], _, err = encodeValue(a[i])
- if err != nil {
- return nil, nil, err
- }
- }
- return listProto(vs...), listType(intType()), nil
-}
-
-func encodeIntArrayFunc(a []int) (*proto3.Value, *sppb.Type, error) {
- v, err := encodeArray(len(a), func(i int) interface{} { return a[i] })
- if err != nil {
- return nil, nil, err
- }
- return v, listType(intType()), nil
-}
-
-func encodeIntArrayReflect(a []int) (*proto3.Value, *sppb.Type, error) {
- v, err := encodeArrayReflect(a)
- if err != nil {
- return nil, nil, err
- }
- return v, listType(intType()), nil
-}
-
-func encodeArrayReflect(a interface{}) (*proto3.Value, error) {
- va := reflect.ValueOf(a)
- len := va.Len()
- vs := make([]*proto3.Value, len)
- var err error
- for i := 0; i < len; i++ {
- vs[i], _, err = encodeValue(va.Index(i).Interface())
- if err != nil {
- return nil, err
- }
- }
- return listProto(vs...), nil
-}