mirror of
https://github.com/restic/restic.git
synced 2026-07-20 14:14:54 +00:00
Update dependencies
Among others, this updates minio-go, so that the new "eu-west-3" zone for AWS is supported.
This commit is contained in:
+14
-36
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -100,8 +100,8 @@ type DatabaseAdminClient struct {
|
||||
// The call options for this service.
|
||||
CallOptions *DatabaseAdminCallOptions
|
||||
|
||||
// The metadata to be sent with each request.
|
||||
Metadata metadata.MD
|
||||
// The x-goog-* metadata to be sent with each request.
|
||||
xGoogMetadata metadata.MD
|
||||
}
|
||||
|
||||
// NewDatabaseAdminClient creates a new database admin client.
|
||||
@@ -154,34 +154,12 @@ func (c *DatabaseAdminClient) Close() error {
|
||||
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.Metadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
||||
}
|
||||
|
||||
// DatabaseAdminInstancePath returns the path for the instance resource.
|
||||
func DatabaseAdminInstancePath(project, instance string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
""
|
||||
}
|
||||
|
||||
// DatabaseAdminDatabasePath returns the path for the database resource.
|
||||
func DatabaseAdminDatabasePath(project, instance, database string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
"/databases/" +
|
||||
database +
|
||||
""
|
||||
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
||||
}
|
||||
|
||||
// ListDatabases lists Cloud Spanner databases.
|
||||
func (c *DatabaseAdminClient) ListDatabases(ctx context.Context, req *databasepb.ListDatabasesRequest, opts ...gax.CallOption) *DatabaseIterator {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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) {
|
||||
@@ -223,7 +201,7 @@ func (c *DatabaseAdminClient) ListDatabases(ctx context.Context, req *databasepb
|
||||
// [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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -241,7 +219,7 @@ func (c *DatabaseAdminClient) CreateDatabase(ctx context.Context, req *databasep
|
||||
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -263,7 +241,7 @@ func (c *DatabaseAdminClient) GetDatabase(ctx context.Context, req *databasepb.G
|
||||
// [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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -281,7 +259,7 @@ func (c *DatabaseAdminClient) UpdateDatabaseDdl(ctx context.Context, req *databa
|
||||
|
||||
// DropDatabase drops (aka deletes) a Cloud Spanner database.
|
||||
func (c *DatabaseAdminClient) DropDatabase(ctx context.Context, req *databasepb.DropDatabaseRequest, opts ...gax.CallOption) error {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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
|
||||
@@ -295,7 +273,7 @@ func (c *DatabaseAdminClient) DropDatabase(ctx context.Context, req *databasepb.
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -315,7 +293,7 @@ func (c *DatabaseAdminClient) GetDatabaseDdl(ctx context.Context, req *databasep
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -335,7 +313,7 @@ func (c *DatabaseAdminClient) SetIamPolicy(ctx context.Context, req *iampb.SetIa
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -356,7 +334,7 @@ func (c *DatabaseAdminClient) GetIamPolicy(ctx context.Context, req *iampb.GetIa
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
|
||||
Generated
Vendored
+11
-11
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -42,7 +42,7 @@ func ExampleDatabaseAdminClient_ListDatabases() {
|
||||
}
|
||||
|
||||
req := &databasepb.ListDatabasesRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
it := c.ListDatabases(ctx, req)
|
||||
for {
|
||||
@@ -66,7 +66,7 @@ func ExampleDatabaseAdminClient_CreateDatabase() {
|
||||
}
|
||||
|
||||
req := &databasepb.CreateDatabaseRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
op, err := c.CreateDatabase(ctx, req)
|
||||
if err != nil {
|
||||
@@ -89,7 +89,7 @@ func ExampleDatabaseAdminClient_GetDatabase() {
|
||||
}
|
||||
|
||||
req := &databasepb.GetDatabaseRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetDatabase(ctx, req)
|
||||
if err != nil {
|
||||
@@ -107,7 +107,7 @@ func ExampleDatabaseAdminClient_UpdateDatabaseDdl() {
|
||||
}
|
||||
|
||||
req := &databasepb.UpdateDatabaseDdlRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
op, err := c.UpdateDatabaseDdl(ctx, req)
|
||||
if err != nil {
|
||||
@@ -126,7 +126,7 @@ func ExampleDatabaseAdminClient_DropDatabase() {
|
||||
}
|
||||
|
||||
req := &databasepb.DropDatabaseRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
err = c.DropDatabase(ctx, req)
|
||||
if err != nil {
|
||||
@@ -142,7 +142,7 @@ func ExampleDatabaseAdminClient_GetDatabaseDdl() {
|
||||
}
|
||||
|
||||
req := &databasepb.GetDatabaseDdlRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetDatabaseDdl(ctx, req)
|
||||
if err != nil {
|
||||
@@ -160,7 +160,7 @@ func ExampleDatabaseAdminClient_SetIamPolicy() {
|
||||
}
|
||||
|
||||
req := &iampb.SetIamPolicyRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.SetIamPolicy(ctx, req)
|
||||
if err != nil {
|
||||
@@ -178,7 +178,7 @@ func ExampleDatabaseAdminClient_GetIamPolicy() {
|
||||
}
|
||||
|
||||
req := &iampb.GetIamPolicyRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetIamPolicy(ctx, req)
|
||||
if err != nil {
|
||||
@@ -196,7 +196,7 @@ func ExampleDatabaseAdminClient_TestIamPermissions() {
|
||||
}
|
||||
|
||||
req := &iampb.TestIamPermissionsRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.TestIamPermissions(ctx, req)
|
||||
if err != nil {
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,7 +17,7 @@
|
||||
// Package database is an auto-generated package for the
|
||||
// Cloud Spanner Database Admin API.
|
||||
//
|
||||
// NOTE: This package is in alpha. It is not stable, and is likely to be subject to changes.
|
||||
// NOTE: This package is in alpha. It is not stable, and is likely to change.
|
||||
//
|
||||
package database // import "cloud.google.com/go/spanner/admin/database/apiv1"
|
||||
|
||||
|
||||
+20
-20
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -214,7 +214,7 @@ func TestDatabaseAdminListDatabases(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &databasepb.ListDatabasesRequest{
|
||||
Parent: formattedParent,
|
||||
}
|
||||
@@ -253,7 +253,7 @@ func TestDatabaseAdminListDatabasesError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &databasepb.ListDatabasesRequest{
|
||||
Parent: formattedParent,
|
||||
}
|
||||
@@ -291,7 +291,7 @@ func TestDatabaseAdminCreateDatabase(t *testing.T) {
|
||||
Result: &longrunningpb.Operation_Response{Response: any},
|
||||
})
|
||||
|
||||
var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var createStatement string = "createStatement552974828"
|
||||
var request = &databasepb.CreateDatabaseRequest{
|
||||
Parent: formattedParent,
|
||||
@@ -336,7 +336,7 @@ func TestDatabaseAdminCreateDatabaseError(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
var formattedParent string = DatabaseAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var createStatement string = "createStatement552974828"
|
||||
var request = &databasepb.CreateDatabaseRequest{
|
||||
Parent: formattedParent,
|
||||
@@ -372,7 +372,7 @@ func TestDatabaseAdminGetDatabase(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedName string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &databasepb.GetDatabaseRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -401,7 +401,7 @@ func TestDatabaseAdminGetDatabaseError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedName string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &databasepb.GetDatabaseRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -436,7 +436,7 @@ func TestDatabaseAdminUpdateDatabaseDdl(t *testing.T) {
|
||||
Result: &longrunningpb.Operation_Response{Response: any},
|
||||
})
|
||||
|
||||
var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var statements []string = nil
|
||||
var request = &databasepb.UpdateDatabaseDdlRequest{
|
||||
Database: formattedDatabase,
|
||||
@@ -478,7 +478,7 @@ func TestDatabaseAdminUpdateDatabaseDdlError(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var statements []string = nil
|
||||
var request = &databasepb.UpdateDatabaseDdlRequest{
|
||||
Database: formattedDatabase,
|
||||
@@ -510,7 +510,7 @@ func TestDatabaseAdminDropDatabase(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &databasepb.DropDatabaseRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -536,7 +536,7 @@ func TestDatabaseAdminDropDatabaseError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &databasepb.DropDatabaseRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -562,7 +562,7 @@ func TestDatabaseAdminGetDatabaseDdl(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &databasepb.GetDatabaseDdlRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -591,7 +591,7 @@ func TestDatabaseAdminGetDatabaseDdlError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedDatabase string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &databasepb.GetDatabaseDdlRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -623,7 +623,7 @@ func TestDatabaseAdminSetIamPolicy(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var policy *iampb.Policy = &iampb.Policy{}
|
||||
var request = &iampb.SetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
@@ -654,7 +654,7 @@ func TestDatabaseAdminSetIamPolicyError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var policy *iampb.Policy = &iampb.Policy{}
|
||||
var request = &iampb.SetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
@@ -688,7 +688,7 @@ func TestDatabaseAdminGetIamPolicy(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &iampb.GetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
}
|
||||
@@ -717,7 +717,7 @@ func TestDatabaseAdminGetIamPolicyError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &iampb.GetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
}
|
||||
@@ -744,7 +744,7 @@ func TestDatabaseAdminTestIamPermissions(t *testing.T) {
|
||||
|
||||
mockDatabaseAdmin.resps = append(mockDatabaseAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var permissions []string = nil
|
||||
var request = &iampb.TestIamPermissionsRequest{
|
||||
Resource: formattedResource,
|
||||
@@ -775,7 +775,7 @@ func TestDatabaseAdminTestIamPermissionsError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockDatabaseAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedResource string = DatabaseAdminDatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var permissions []string = nil
|
||||
var request = &iampb.TestIamPermissionsRequest{
|
||||
Resource: formattedResource,
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package database
|
||||
|
||||
// DatabaseAdminInstancePath returns the path for the instance resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s/instances/%s", project, instance)
|
||||
// instead.
|
||||
func DatabaseAdminInstancePath(project, instance string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
""
|
||||
}
|
||||
|
||||
// DatabaseAdminDatabasePath returns the path for the database resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, database)
|
||||
// instead.
|
||||
func DatabaseAdminDatabasePath(project, instance, database string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
"/databases/" +
|
||||
database +
|
||||
""
|
||||
}
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,7 +17,7 @@
|
||||
// Package instance is an auto-generated package for the
|
||||
// Cloud Spanner Instance Admin API.
|
||||
//
|
||||
// NOTE: This package is in alpha. It is not stable, and is likely to be subject to changes.
|
||||
// NOTE: This package is in alpha. It is not stable, and is likely to change.
|
||||
//
|
||||
package instance // import "cloud.google.com/go/spanner/admin/instance/apiv1"
|
||||
|
||||
|
||||
+15
-43
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -102,8 +102,8 @@ type InstanceAdminClient struct {
|
||||
// The call options for this service.
|
||||
CallOptions *InstanceAdminCallOptions
|
||||
|
||||
// The metadata to be sent with each request.
|
||||
Metadata metadata.MD
|
||||
// The x-goog-* metadata to be sent with each request.
|
||||
xGoogMetadata metadata.MD
|
||||
}
|
||||
|
||||
// NewInstanceAdminClient creates a new instance admin client.
|
||||
@@ -172,40 +172,12 @@ func (c *InstanceAdminClient) Close() error {
|
||||
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.Metadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
||||
}
|
||||
|
||||
// InstanceAdminProjectPath returns the path for the project resource.
|
||||
func InstanceAdminProjectPath(project string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
""
|
||||
}
|
||||
|
||||
// InstanceAdminInstanceConfigPath returns the path for the instance config resource.
|
||||
func InstanceAdminInstanceConfigPath(project, instanceConfig string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instanceConfigs/" +
|
||||
instanceConfig +
|
||||
""
|
||||
}
|
||||
|
||||
// InstanceAdminInstancePath returns the path for the instance resource.
|
||||
func InstanceAdminInstancePath(project, instance string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
""
|
||||
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
||||
}
|
||||
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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) {
|
||||
@@ -240,7 +212,7 @@ func (c *InstanceAdminClient) ListInstanceConfigs(ctx context.Context, req *inst
|
||||
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -256,7 +228,7 @@ func (c *InstanceAdminClient) GetInstanceConfig(ctx context.Context, req *instan
|
||||
|
||||
// ListInstances lists all instances in the given project.
|
||||
func (c *InstanceAdminClient) ListInstances(ctx context.Context, req *instancepb.ListInstancesRequest, opts ...gax.CallOption) *InstanceIterator {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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) {
|
||||
@@ -291,7 +263,7 @@ func (c *InstanceAdminClient) ListInstances(ctx context.Context, req *instancepb
|
||||
|
||||
// GetInstance gets information about a particular instance.
|
||||
func (c *InstanceAdminClient) GetInstance(ctx context.Context, req *instancepb.GetInstanceRequest, opts ...gax.CallOption) (*instancepb.Instance, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -345,7 +317,7 @@ func (c *InstanceAdminClient) GetInstance(ctx context.Context, req *instancepb.G
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -406,7 +378,7 @@ func (c *InstanceAdminClient) CreateInstance(ctx context.Context, req *instancep
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -434,7 +406,7 @@ func (c *InstanceAdminClient) UpdateInstance(ctx context.Context, req *instancep
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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
|
||||
@@ -450,7 +422,7 @@ func (c *InstanceAdminClient) DeleteInstance(ctx context.Context, req *instancep
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -470,7 +442,7 @@ func (c *InstanceAdminClient) SetIamPolicy(ctx context.Context, req *iampb.SetIa
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
@@ -491,7 +463,7 @@ func (c *InstanceAdminClient) GetIamPolicy(ctx context.Context, req *iampb.GetIa
|
||||
// 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 = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
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 {
|
||||
|
||||
Generated
Vendored
+12
-12
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -42,7 +42,7 @@ func ExampleInstanceAdminClient_ListInstanceConfigs() {
|
||||
}
|
||||
|
||||
req := &instancepb.ListInstanceConfigsRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
it := c.ListInstanceConfigs(ctx, req)
|
||||
for {
|
||||
@@ -66,7 +66,7 @@ func ExampleInstanceAdminClient_GetInstanceConfig() {
|
||||
}
|
||||
|
||||
req := &instancepb.GetInstanceConfigRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetInstanceConfig(ctx, req)
|
||||
if err != nil {
|
||||
@@ -84,7 +84,7 @@ func ExampleInstanceAdminClient_ListInstances() {
|
||||
}
|
||||
|
||||
req := &instancepb.ListInstancesRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
it := c.ListInstances(ctx, req)
|
||||
for {
|
||||
@@ -108,7 +108,7 @@ func ExampleInstanceAdminClient_GetInstance() {
|
||||
}
|
||||
|
||||
req := &instancepb.GetInstanceRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetInstance(ctx, req)
|
||||
if err != nil {
|
||||
@@ -126,7 +126,7 @@ func ExampleInstanceAdminClient_CreateInstance() {
|
||||
}
|
||||
|
||||
req := &instancepb.CreateInstanceRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
op, err := c.CreateInstance(ctx, req)
|
||||
if err != nil {
|
||||
@@ -149,7 +149,7 @@ func ExampleInstanceAdminClient_UpdateInstance() {
|
||||
}
|
||||
|
||||
req := &instancepb.UpdateInstanceRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
op, err := c.UpdateInstance(ctx, req)
|
||||
if err != nil {
|
||||
@@ -172,7 +172,7 @@ func ExampleInstanceAdminClient_DeleteInstance() {
|
||||
}
|
||||
|
||||
req := &instancepb.DeleteInstanceRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
err = c.DeleteInstance(ctx, req)
|
||||
if err != nil {
|
||||
@@ -188,7 +188,7 @@ func ExampleInstanceAdminClient_SetIamPolicy() {
|
||||
}
|
||||
|
||||
req := &iampb.SetIamPolicyRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.SetIamPolicy(ctx, req)
|
||||
if err != nil {
|
||||
@@ -206,7 +206,7 @@ func ExampleInstanceAdminClient_GetIamPolicy() {
|
||||
}
|
||||
|
||||
req := &iampb.GetIamPolicyRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetIamPolicy(ctx, req)
|
||||
if err != nil {
|
||||
@@ -224,7 +224,7 @@ func ExampleInstanceAdminClient_TestIamPermissions() {
|
||||
}
|
||||
|
||||
req := &iampb.TestIamPermissionsRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.TestIamPermissions(ctx, req)
|
||||
if err != nil {
|
||||
|
||||
+20
-20
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -227,7 +227,7 @@ func TestInstanceAdminListInstanceConfigs(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]")
|
||||
var request = &instancepb.ListInstanceConfigsRequest{
|
||||
Parent: formattedParent,
|
||||
}
|
||||
@@ -266,7 +266,7 @@ func TestInstanceAdminListInstanceConfigsError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]")
|
||||
var request = &instancepb.ListInstanceConfigsRequest{
|
||||
Parent: formattedParent,
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func TestInstanceAdminGetInstanceConfig(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedName string = InstanceAdminInstanceConfigPath("[PROJECT]", "[INSTANCE_CONFIG]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instanceConfigs/%s", "[PROJECT]", "[INSTANCE_CONFIG]")
|
||||
var request = &instancepb.GetInstanceConfigRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func TestInstanceAdminGetInstanceConfigError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedName string = InstanceAdminInstanceConfigPath("[PROJECT]", "[INSTANCE_CONFIG]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instanceConfigs/%s", "[PROJECT]", "[INSTANCE_CONFIG]")
|
||||
var request = &instancepb.GetInstanceConfigRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -360,7 +360,7 @@ func TestInstanceAdminListInstances(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]")
|
||||
var request = &instancepb.ListInstancesRequest{
|
||||
Parent: formattedParent,
|
||||
}
|
||||
@@ -399,7 +399,7 @@ func TestInstanceAdminListInstancesError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]")
|
||||
var request = &instancepb.ListInstancesRequest{
|
||||
Parent: formattedParent,
|
||||
}
|
||||
@@ -435,7 +435,7 @@ func TestInstanceAdminGetInstance(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &instancepb.GetInstanceRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -464,7 +464,7 @@ func TestInstanceAdminGetInstanceError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &instancepb.GetInstanceRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -508,7 +508,7 @@ func TestInstanceAdminCreateInstance(t *testing.T) {
|
||||
Result: &longrunningpb.Operation_Response{Response: any},
|
||||
})
|
||||
|
||||
var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]")
|
||||
var instanceId string = "instanceId-2101995259"
|
||||
var instance *instancepb.Instance = &instancepb.Instance{}
|
||||
var request = &instancepb.CreateInstanceRequest{
|
||||
@@ -555,7 +555,7 @@ func TestInstanceAdminCreateInstanceError(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
var formattedParent string = InstanceAdminProjectPath("[PROJECT]")
|
||||
var formattedParent string = fmt.Sprintf("projects/%s", "[PROJECT]")
|
||||
var instanceId string = "instanceId-2101995259"
|
||||
var instance *instancepb.Instance = &instancepb.Instance{}
|
||||
var request = &instancepb.CreateInstanceRequest{
|
||||
@@ -685,7 +685,7 @@ func TestInstanceAdminDeleteInstance(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &instancepb.DeleteInstanceRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -711,7 +711,7 @@ func TestInstanceAdminDeleteInstanceError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedName string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &instancepb.DeleteInstanceRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -742,7 +742,7 @@ func TestInstanceAdminSetIamPolicy(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var policy *iampb.Policy = &iampb.Policy{}
|
||||
var request = &iampb.SetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
@@ -773,7 +773,7 @@ func TestInstanceAdminSetIamPolicyError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var policy *iampb.Policy = &iampb.Policy{}
|
||||
var request = &iampb.SetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
@@ -807,7 +807,7 @@ func TestInstanceAdminGetIamPolicy(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &iampb.GetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
}
|
||||
@@ -836,7 +836,7 @@ func TestInstanceAdminGetIamPolicyError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var request = &iampb.GetIamPolicyRequest{
|
||||
Resource: formattedResource,
|
||||
}
|
||||
@@ -863,7 +863,7 @@ func TestInstanceAdminTestIamPermissions(t *testing.T) {
|
||||
|
||||
mockInstanceAdmin.resps = append(mockInstanceAdmin.resps[:0], expectedResponse)
|
||||
|
||||
var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var permissions []string = nil
|
||||
var request = &iampb.TestIamPermissionsRequest{
|
||||
Resource: formattedResource,
|
||||
@@ -894,7 +894,7 @@ func TestInstanceAdminTestIamPermissionsError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockInstanceAdmin.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedResource string = InstanceAdminInstancePath("[PROJECT]", "[INSTANCE]")
|
||||
var formattedResource string = fmt.Sprintf("projects/%s/instances/%s", "[PROJECT]", "[INSTANCE]")
|
||||
var permissions []string = nil
|
||||
var request = &iampb.TestIamPermissionsRequest{
|
||||
Resource: formattedResource,
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package instance
|
||||
|
||||
// InstanceAdminProjectPath returns the path for the project resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s", project)
|
||||
// instead.
|
||||
func InstanceAdminProjectPath(project string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
""
|
||||
}
|
||||
|
||||
// InstanceAdminInstanceConfigPath returns the path for the instance config resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s/instanceConfigs/%s", project, instanceConfig)
|
||||
// instead.
|
||||
func InstanceAdminInstanceConfigPath(project, instanceConfig string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instanceConfigs/" +
|
||||
instanceConfig +
|
||||
""
|
||||
}
|
||||
|
||||
// InstanceAdminInstancePath returns the path for the instance resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s/instances/%s", project, instance)
|
||||
// instead.
|
||||
func InstanceAdminInstancePath(project, instance string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
""
|
||||
}
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,7 +17,7 @@
|
||||
// Package spanner is an auto-generated package for the
|
||||
// Cloud Spanner API.
|
||||
//
|
||||
// NOTE: This package is in alpha. It is not stable, and is likely to be subject to changes.
|
||||
// NOTE: This package is in alpha. It is not stable, and is likely to change.
|
||||
//
|
||||
// Cloud Spanner is a managed, mission-critical, globally consistent and
|
||||
// scalable relational database service.
|
||||
|
||||
+24
-24
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -243,7 +243,7 @@ func TestSpannerCreateSession(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedDatabase string = DatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &spannerpb.CreateSessionRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func TestSpannerCreateSessionError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedDatabase string = DatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &spannerpb.CreateSessionRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -302,7 +302,7 @@ func TestSpannerGetSession(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedName string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var request = &spannerpb.GetSessionRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -331,7 +331,7 @@ func TestSpannerGetSessionError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedName string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var request = &spannerpb.GetSessionRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -364,7 +364,7 @@ func TestSpannerListSessions(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedDatabase string = DatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &spannerpb.ListSessionsRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -403,7 +403,7 @@ func TestSpannerListSessionsError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedDatabase string = DatabasePath("[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var formattedDatabase string = fmt.Sprintf("projects/%s/instances/%s/databases/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]")
|
||||
var request = &spannerpb.ListSessionsRequest{
|
||||
Database: formattedDatabase,
|
||||
}
|
||||
@@ -430,7 +430,7 @@ func TestSpannerDeleteSession(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedName string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var request = &spannerpb.DeleteSessionRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -456,7 +456,7 @@ func TestSpannerDeleteSessionError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedName string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedName string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var request = &spannerpb.DeleteSessionRequest{
|
||||
Name: formattedName,
|
||||
}
|
||||
@@ -482,7 +482,7 @@ func TestSpannerExecuteSql(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var sql string = "sql114126"
|
||||
var request = &spannerpb.ExecuteSqlRequest{
|
||||
Session: formattedSession,
|
||||
@@ -513,7 +513,7 @@ func TestSpannerExecuteSqlError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var sql string = "sql114126"
|
||||
var request = &spannerpb.ExecuteSqlRequest{
|
||||
Session: formattedSession,
|
||||
@@ -547,7 +547,7 @@ func TestSpannerExecuteStreamingSql(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var sql string = "sql114126"
|
||||
var request = &spannerpb.ExecuteSqlRequest{
|
||||
Session: formattedSession,
|
||||
@@ -582,7 +582,7 @@ func TestSpannerExecuteStreamingSqlError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var sql string = "sql114126"
|
||||
var request = &spannerpb.ExecuteSqlRequest{
|
||||
Session: formattedSession,
|
||||
@@ -615,7 +615,7 @@ func TestSpannerRead(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var table string = "table110115790"
|
||||
var columns []string = nil
|
||||
var keySet *spannerpb.KeySet = &spannerpb.KeySet{}
|
||||
@@ -650,7 +650,7 @@ func TestSpannerReadError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var table string = "table110115790"
|
||||
var columns []string = nil
|
||||
var keySet *spannerpb.KeySet = &spannerpb.KeySet{}
|
||||
@@ -688,7 +688,7 @@ func TestSpannerStreamingRead(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var table string = "table110115790"
|
||||
var columns []string = nil
|
||||
var keySet *spannerpb.KeySet = &spannerpb.KeySet{}
|
||||
@@ -727,7 +727,7 @@ func TestSpannerStreamingReadError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var table string = "table110115790"
|
||||
var columns []string = nil
|
||||
var keySet *spannerpb.KeySet = &spannerpb.KeySet{}
|
||||
@@ -767,7 +767,7 @@ func TestSpannerBeginTransaction(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var options *spannerpb.TransactionOptions = &spannerpb.TransactionOptions{}
|
||||
var request = &spannerpb.BeginTransactionRequest{
|
||||
Session: formattedSession,
|
||||
@@ -798,7 +798,7 @@ func TestSpannerBeginTransactionError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var options *spannerpb.TransactionOptions = &spannerpb.TransactionOptions{}
|
||||
var request = &spannerpb.BeginTransactionRequest{
|
||||
Session: formattedSession,
|
||||
@@ -827,7 +827,7 @@ func TestSpannerCommit(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var mutations []*spannerpb.Mutation = nil
|
||||
var request = &spannerpb.CommitRequest{
|
||||
Session: formattedSession,
|
||||
@@ -858,7 +858,7 @@ func TestSpannerCommitError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var mutations []*spannerpb.Mutation = nil
|
||||
var request = &spannerpb.CommitRequest{
|
||||
Session: formattedSession,
|
||||
@@ -887,7 +887,7 @@ func TestSpannerRollback(t *testing.T) {
|
||||
|
||||
mockSpanner.resps = append(mockSpanner.resps[:0], expectedResponse)
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var transactionId []byte = []byte("28")
|
||||
var request = &spannerpb.RollbackRequest{
|
||||
Session: formattedSession,
|
||||
@@ -915,7 +915,7 @@ func TestSpannerRollbackError(t *testing.T) {
|
||||
errCode := codes.PermissionDenied
|
||||
mockSpanner.err = gstatus.Error(errCode, "test error")
|
||||
|
||||
var formattedSession string = SessionPath("[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var formattedSession string = fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", "[PROJECT]", "[INSTANCE]", "[DATABASE]", "[SESSION]")
|
||||
var transactionId []byte = []byte("28")
|
||||
var request = &spannerpb.RollbackRequest{
|
||||
Session: formattedSession,
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package spanner
|
||||
|
||||
// DatabasePath returns the path for the database resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, database)
|
||||
// instead.
|
||||
func DatabasePath(project, instance, database string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
"/databases/" +
|
||||
database +
|
||||
""
|
||||
}
|
||||
|
||||
// SessionPath returns the path for the session resource.
|
||||
//
|
||||
// Deprecated: Use
|
||||
// fmt.Sprintf("projects/%s/instances/%s/databases/%s/sessions/%s", project, instance, database, session)
|
||||
// instead.
|
||||
func SessionPath(project, instance, database, session string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
"/databases/" +
|
||||
database +
|
||||
"/sessions/" +
|
||||
session +
|
||||
""
|
||||
}
|
||||
+16
-42
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -106,8 +106,8 @@ type Client struct {
|
||||
// The call options for this service.
|
||||
CallOptions *CallOptions
|
||||
|
||||
// The metadata to be sent with each request.
|
||||
Metadata metadata.MD
|
||||
// The x-goog-* metadata to be sent with each request.
|
||||
xGoogMetadata metadata.MD
|
||||
}
|
||||
|
||||
// NewClient creates a new spanner client.
|
||||
@@ -148,33 +148,7 @@ func (c *Client) Close() error {
|
||||
func (c *Client) SetGoogleClientInfo(keyval ...string) {
|
||||
kv := append([]string{"gl-go", version.Go()}, keyval...)
|
||||
kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version)
|
||||
c.Metadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
||||
}
|
||||
|
||||
// DatabasePath returns the path for the database resource.
|
||||
func DatabasePath(project, instance, database string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
"/databases/" +
|
||||
database +
|
||||
""
|
||||
}
|
||||
|
||||
// SessionPath returns the path for the session resource.
|
||||
func SessionPath(project, instance, database, session string) string {
|
||||
return "" +
|
||||
"projects/" +
|
||||
project +
|
||||
"/instances/" +
|
||||
instance +
|
||||
"/databases/" +
|
||||
database +
|
||||
"/sessions/" +
|
||||
session +
|
||||
""
|
||||
c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))
|
||||
}
|
||||
|
||||
// CreateSession creates a new session. A session can be used to perform
|
||||
@@ -197,7 +171,7 @@ func SessionPath(project, instance, database, session string) string {
|
||||
// Idle sessions can be kept alive by sending a trivial SQL query
|
||||
// periodically, e.g., "SELECT 1".
|
||||
func (c *Client) CreateSession(ctx context.Context, req *spannerpb.CreateSessionRequest, opts ...gax.CallOption) (*spannerpb.Session, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.CreateSession[0:len(c.CallOptions.CreateSession):len(c.CallOptions.CreateSession)], opts...)
|
||||
var resp *spannerpb.Session
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -215,7 +189,7 @@ func (c *Client) CreateSession(ctx context.Context, req *spannerpb.CreateSession
|
||||
// This is mainly useful for determining whether a session is still
|
||||
// alive.
|
||||
func (c *Client) GetSession(ctx context.Context, req *spannerpb.GetSessionRequest, opts ...gax.CallOption) (*spannerpb.Session, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.GetSession[0:len(c.CallOptions.GetSession):len(c.CallOptions.GetSession)], opts...)
|
||||
var resp *spannerpb.Session
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -231,7 +205,7 @@ func (c *Client) GetSession(ctx context.Context, req *spannerpb.GetSessionReques
|
||||
|
||||
// ListSessions lists all sessions in a given database.
|
||||
func (c *Client) ListSessions(ctx context.Context, req *spannerpb.ListSessionsRequest, opts ...gax.CallOption) *SessionIterator {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.ListSessions[0:len(c.CallOptions.ListSessions):len(c.CallOptions.ListSessions)], opts...)
|
||||
it := &SessionIterator{}
|
||||
it.InternalFetch = func(pageSize int, pageToken string) ([]*spannerpb.Session, string, error) {
|
||||
@@ -266,7 +240,7 @@ func (c *Client) ListSessions(ctx context.Context, req *spannerpb.ListSessionsRe
|
||||
|
||||
// DeleteSession ends a session, releasing server resources associated with it.
|
||||
func (c *Client) DeleteSession(ctx context.Context, req *spannerpb.DeleteSessionRequest, opts ...gax.CallOption) error {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.DeleteSession[0:len(c.CallOptions.DeleteSession):len(c.CallOptions.DeleteSession)], opts...)
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
var err error
|
||||
@@ -288,7 +262,7 @@ func (c *Client) DeleteSession(ctx context.Context, req *spannerpb.DeleteSession
|
||||
// Larger result sets can be fetched in streaming fashion by calling
|
||||
// [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] instead.
|
||||
func (c *Client) ExecuteSql(ctx context.Context, req *spannerpb.ExecuteSqlRequest, opts ...gax.CallOption) (*spannerpb.ResultSet, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.ExecuteSql[0:len(c.CallOptions.ExecuteSql):len(c.CallOptions.ExecuteSql)], opts...)
|
||||
var resp *spannerpb.ResultSet
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -308,7 +282,7 @@ func (c *Client) ExecuteSql(ctx context.Context, req *spannerpb.ExecuteSqlReques
|
||||
// individual row in the result set can exceed 100 MiB, and no
|
||||
// column value can exceed 10 MiB.
|
||||
func (c *Client) ExecuteStreamingSql(ctx context.Context, req *spannerpb.ExecuteSqlRequest, opts ...gax.CallOption) (spannerpb.Spanner_ExecuteStreamingSqlClient, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.ExecuteStreamingSql[0:len(c.CallOptions.ExecuteStreamingSql):len(c.CallOptions.ExecuteStreamingSql)], opts...)
|
||||
var resp spannerpb.Spanner_ExecuteStreamingSqlClient
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -336,7 +310,7 @@ func (c *Client) ExecuteStreamingSql(ctx context.Context, req *spannerpb.Execute
|
||||
// Larger result sets can be yielded in streaming fashion by calling
|
||||
// [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead.
|
||||
func (c *Client) Read(ctx context.Context, req *spannerpb.ReadRequest, opts ...gax.CallOption) (*spannerpb.ResultSet, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.Read[0:len(c.CallOptions.Read):len(c.CallOptions.Read)], opts...)
|
||||
var resp *spannerpb.ResultSet
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -356,7 +330,7 @@ func (c *Client) Read(ctx context.Context, req *spannerpb.ReadRequest, opts ...g
|
||||
// the result set can exceed 100 MiB, and no column value can exceed
|
||||
// 10 MiB.
|
||||
func (c *Client) StreamingRead(ctx context.Context, req *spannerpb.ReadRequest, opts ...gax.CallOption) (spannerpb.Spanner_StreamingReadClient, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.StreamingRead[0:len(c.CallOptions.StreamingRead):len(c.CallOptions.StreamingRead)], opts...)
|
||||
var resp spannerpb.Spanner_StreamingReadClient
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -375,7 +349,7 @@ func (c *Client) StreamingRead(ctx context.Context, req *spannerpb.ReadRequest,
|
||||
// [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a
|
||||
// side-effect.
|
||||
func (c *Client) BeginTransaction(ctx context.Context, req *spannerpb.BeginTransactionRequest, opts ...gax.CallOption) (*spannerpb.Transaction, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.BeginTransaction[0:len(c.CallOptions.BeginTransaction):len(c.CallOptions.BeginTransaction)], opts...)
|
||||
var resp *spannerpb.Transaction
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -398,7 +372,7 @@ func (c *Client) BeginTransaction(ctx context.Context, req *spannerpb.BeginTrans
|
||||
// reasons. If Commit returns ABORTED, the caller should re-attempt
|
||||
// the transaction from the beginning, re-using the same session.
|
||||
func (c *Client) Commit(ctx context.Context, req *spannerpb.CommitRequest, opts ...gax.CallOption) (*spannerpb.CommitResponse, error) {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.Commit[0:len(c.CallOptions.Commit):len(c.CallOptions.Commit)], opts...)
|
||||
var resp *spannerpb.CommitResponse
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
@@ -421,7 +395,7 @@ func (c *Client) Commit(ctx context.Context, req *spannerpb.CommitRequest, opts
|
||||
// transaction was already aborted, or the transaction is not
|
||||
// found. Rollback never returns ABORTED.
|
||||
func (c *Client) Rollback(ctx context.Context, req *spannerpb.RollbackRequest, opts ...gax.CallOption) error {
|
||||
ctx = insertMetadata(ctx, c.Metadata)
|
||||
ctx = insertMetadata(ctx, c.xGoogMetadata)
|
||||
opts = append(c.CallOptions.Rollback[0:len(c.CallOptions.Rollback):len(c.CallOptions.Rollback)], opts...)
|
||||
err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
|
||||
var err error
|
||||
|
||||
+13
-13
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017, Google Inc. All rights reserved.
|
||||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -43,7 +43,7 @@ func ExampleClient_CreateSession() {
|
||||
}
|
||||
|
||||
req := &spannerpb.CreateSessionRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.CreateSession(ctx, req)
|
||||
if err != nil {
|
||||
@@ -61,7 +61,7 @@ func ExampleClient_GetSession() {
|
||||
}
|
||||
|
||||
req := &spannerpb.GetSessionRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.GetSession(ctx, req)
|
||||
if err != nil {
|
||||
@@ -79,7 +79,7 @@ func ExampleClient_ListSessions() {
|
||||
}
|
||||
|
||||
req := &spannerpb.ListSessionsRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
it := c.ListSessions(ctx, req)
|
||||
for {
|
||||
@@ -103,7 +103,7 @@ func ExampleClient_DeleteSession() {
|
||||
}
|
||||
|
||||
req := &spannerpb.DeleteSessionRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
err = c.DeleteSession(ctx, req)
|
||||
if err != nil {
|
||||
@@ -119,7 +119,7 @@ func ExampleClient_ExecuteSql() {
|
||||
}
|
||||
|
||||
req := &spannerpb.ExecuteSqlRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.ExecuteSql(ctx, req)
|
||||
if err != nil {
|
||||
@@ -137,7 +137,7 @@ func ExampleClient_ExecuteStreamingSql() {
|
||||
}
|
||||
|
||||
req := &spannerpb.ExecuteSqlRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
stream, err := c.ExecuteStreamingSql(ctx, req)
|
||||
if err != nil {
|
||||
@@ -164,7 +164,7 @@ func ExampleClient_Read() {
|
||||
}
|
||||
|
||||
req := &spannerpb.ReadRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.Read(ctx, req)
|
||||
if err != nil {
|
||||
@@ -182,7 +182,7 @@ func ExampleClient_StreamingRead() {
|
||||
}
|
||||
|
||||
req := &spannerpb.ReadRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
stream, err := c.StreamingRead(ctx, req)
|
||||
if err != nil {
|
||||
@@ -209,7 +209,7 @@ func ExampleClient_BeginTransaction() {
|
||||
}
|
||||
|
||||
req := &spannerpb.BeginTransactionRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.BeginTransaction(ctx, req)
|
||||
if err != nil {
|
||||
@@ -227,7 +227,7 @@ func ExampleClient_Commit() {
|
||||
}
|
||||
|
||||
req := &spannerpb.CommitRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
resp, err := c.Commit(ctx, req)
|
||||
if err != nil {
|
||||
@@ -245,7 +245,7 @@ func ExampleClient_Rollback() {
|
||||
}
|
||||
|
||||
req := &spannerpb.RollbackRequest{
|
||||
// TODO: Fill request struct fields.
|
||||
// TODO: Fill request struct fields.
|
||||
}
|
||||
err = c.Rollback(ctx, req)
|
||||
if err != nil {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package spanner
|
||||
|
||||
// numChannels is the default value for NumChannels of client
|
||||
const numChannels = 1
|
||||
+28
-18
@@ -41,9 +41,6 @@ const (
|
||||
// xGoogHeaderKey is the name of the metadata header used to indicate client
|
||||
// information.
|
||||
xGoogHeaderKey = "x-goog-api-client"
|
||||
|
||||
// numChannels is the default value for NumChannels of client
|
||||
numChannels = 4
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -82,8 +79,8 @@ type Client struct {
|
||||
|
||||
// ClientConfig has configurations for the client.
|
||||
type ClientConfig struct {
|
||||
// NumChannels is the number of GRPC channels.
|
||||
// If zero, numChannels is used.
|
||||
// NumChannels is the number of gRPC channels.
|
||||
// If zero, a reasonable default is used based on the execution environment.
|
||||
NumChannels int
|
||||
co []option.ClientOption
|
||||
// SessionPoolConfig is the configuration for session pool.
|
||||
@@ -114,7 +111,10 @@ func NewClient(ctx context.Context, database string, opts ...option.ClientOption
|
||||
|
||||
// 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) {
|
||||
func NewClientWithConfig(ctx context.Context, database string, config ClientConfig, opts ...option.ClientOption) (_ *Client, err error) {
|
||||
ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.NewClient")
|
||||
defer func() { traceEndSpan(ctx, err) }()
|
||||
|
||||
// Validate database path.
|
||||
if err := validDatabaseName(database); err != nil {
|
||||
return nil, err
|
||||
@@ -125,7 +125,17 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
|
||||
resourcePrefixHeader, database,
|
||||
xGoogHeaderKey, xGoogHeaderVal),
|
||||
}
|
||||
allOpts := []option.ClientOption{option.WithEndpoint(prodAddr), option.WithScopes(Scope), option.WithGRPCDialOption(grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20)))}
|
||||
allOpts := []option.ClientOption{
|
||||
option.WithEndpoint(prodAddr),
|
||||
option.WithScopes(Scope),
|
||||
option.WithGRPCDialOption(
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallSendMsgSize(100<<20),
|
||||
grpc.MaxCallRecvMsgSize(100<<20),
|
||||
),
|
||||
),
|
||||
}
|
||||
allOpts = append(allOpts, openCensusOptions()...)
|
||||
allOpts = append(allOpts, opts...)
|
||||
// Prepare gRPC channels.
|
||||
if config.NumChannels == 0 {
|
||||
@@ -138,10 +148,6 @@ func NewClientWithConfig(ctx context.Context, database string, config ClientConf
|
||||
if config.MaxBurst == 0 {
|
||||
config.MaxBurst = 10
|
||||
}
|
||||
// Default MaxSessionAge
|
||||
if config.maxSessionAge == 0 {
|
||||
config.maxSessionAge = time.Minute * 30
|
||||
}
|
||||
for i := 0; i < config.NumChannels; i++ {
|
||||
conn, err := gtransport.Dial(ctx, allOpts...)
|
||||
if err != nil {
|
||||
@@ -237,7 +243,9 @@ func checkNestedTxn(ctx context.Context) 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) {
|
||||
func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Context, *ReadWriteTransaction) error) (commitTimestamp time.Time, err error) {
|
||||
ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.ReadWriteTransaction")
|
||||
defer func() { traceEndSpan(ctx, err) }()
|
||||
if err := checkNestedTxn(ctx); err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
@@ -245,7 +253,7 @@ func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Contex
|
||||
ts time.Time
|
||||
sh *sessionHandle
|
||||
)
|
||||
err := runRetryable(ctx, func(ctx context.Context) error {
|
||||
err = runRetryableNoWrap(ctx, func(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
t *ReadWriteTransaction
|
||||
@@ -267,15 +275,14 @@ func (c *Client) ReadWriteTransaction(ctx context.Context, f func(context.Contex
|
||||
}
|
||||
}
|
||||
t.txReadOnly.txReadEnv = t
|
||||
tracePrintf(ctx, map[string]interface{}{"transactionID": string(sh.getTransactionID())},
|
||||
"Starting transaction attempt")
|
||||
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
|
||||
return err
|
||||
})
|
||||
if sh != nil {
|
||||
sh.recycle()
|
||||
@@ -310,7 +317,7 @@ func ApplyAtLeastOnce() ApplyOption {
|
||||
}
|
||||
|
||||
// Apply applies a list of mutations atomically to the database.
|
||||
func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption) (time.Time, error) {
|
||||
func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption) (commitTimestamp time.Time, err error) {
|
||||
ao := &applyOption{}
|
||||
for _, opt := range opts {
|
||||
opt(ao)
|
||||
@@ -321,6 +328,9 @@ func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.Apply")
|
||||
defer func() { traceEndSpan(ctx, err) }()
|
||||
t := &writeOnlyTransaction{c.idleSessions}
|
||||
return t.applyAtLeastOnce(ctx, ms...)
|
||||
}
|
||||
|
||||
+6
@@ -302,6 +302,12 @@ mutations, which will all be executed at the end of the transaction:
|
||||
return nil
|
||||
})
|
||||
|
||||
Tracing
|
||||
|
||||
This client has been instrumented to use OpenCensus tracing (http://opencensus.io).
|
||||
To enable tracing, see "Enabling Tracing for a Program" at
|
||||
https://godoc.org/go.opencensus.io/trace. OpenCensus tracing requires Go 1.8 or higher.
|
||||
|
||||
Authentication
|
||||
|
||||
See examples of authorization and authentication at
|
||||
|
||||
+9
-2
@@ -19,6 +19,7 @@ package spanner
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -74,10 +75,16 @@ func toSpannerErrorWithMetadata(err error, trailers metadata.MD) error {
|
||||
}
|
||||
return se
|
||||
}
|
||||
if grpc.Code(err) == codes.Unknown {
|
||||
switch {
|
||||
case err == context.DeadlineExceeded:
|
||||
return &Error{codes.DeadlineExceeded, err.Error(), trailers}
|
||||
case err == context.Canceled:
|
||||
return &Error{codes.Canceled, err.Error(), trailers}
|
||||
case grpc.Code(err) == codes.Unknown:
|
||||
return &Error{codes.Unknown, err.Error(), trailers}
|
||||
default:
|
||||
return &Error{grpc.Code(err), grpc.ErrorDesc(err), trailers}
|
||||
}
|
||||
return &Error{grpc.Code(err), grpc.ErrorDesc(err), trailers}
|
||||
}
|
||||
|
||||
// ErrCode extracts the canonical error code from a Go error.
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
func TestToSpannerError(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
err error
|
||||
wantCode codes.Code
|
||||
}{
|
||||
{errors.New("wha?"), codes.Unknown},
|
||||
{context.Canceled, codes.Canceled},
|
||||
{context.DeadlineExceeded, codes.DeadlineExceeded},
|
||||
{grpc.Errorf(codes.ResourceExhausted, "so tired"), codes.ResourceExhausted},
|
||||
{spannerErrorf(codes.InvalidArgument, "bad"), codes.InvalidArgument},
|
||||
} {
|
||||
err := toSpannerError(test.err)
|
||||
if got, want := err.(*Error).Code, test.wantCode; got != want {
|
||||
t.Errorf("%v: got %s, want %s", test.err, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -427,6 +427,22 @@ func ExampleReadOnlyTransaction_ReadUsingIndex() {
|
||||
_ = iter // TODO: iterate using Next or Do.
|
||||
}
|
||||
|
||||
func ExampleReadOnlyTransaction_ReadWithOptions() {
|
||||
ctx := context.Background()
|
||||
client, err := spanner.NewClient(ctx, myDB)
|
||||
if err != nil {
|
||||
// TODO: Handle error.
|
||||
}
|
||||
// Use an index, and limit to 100 rows at most.
|
||||
iter := client.Single().ReadWithOptions(ctx, "Users",
|
||||
spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}),
|
||||
[]string{"name", "email"}, &spanner.ReadOptions{
|
||||
Index: "UsersByEmail",
|
||||
Limit: 100,
|
||||
})
|
||||
_ = iter // TODO: iterate using Next or Do.
|
||||
}
|
||||
|
||||
func ExampleReadOnlyTransaction_ReadRow() {
|
||||
ctx := context.Background()
|
||||
client, err := spanner.NewClient(ctx, myDB)
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package spanner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
ocgrpc "go.opencensus.io/plugin/grpc"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func openCensusOptions() []option.ClientOption {
|
||||
return []option.ClientOption{
|
||||
option.WithGRPCDialOption(grpc.WithStatsHandler(ocgrpc.NewClientStatsHandler())),
|
||||
}
|
||||
}
|
||||
|
||||
func traceStartSpan(ctx context.Context, name string) context.Context {
|
||||
return trace.StartSpan(ctx, name)
|
||||
}
|
||||
|
||||
func traceEndSpan(ctx context.Context, err error) {
|
||||
span := trace.FromContext(ctx)
|
||||
if err != nil {
|
||||
span.SetStatus(trace.Status{Message: err.Error()})
|
||||
}
|
||||
trace.EndSpan(ctx)
|
||||
}
|
||||
|
||||
func tracePrintf(ctx context.Context, attrMap map[string]interface{}, format string, args ...interface{}) {
|
||||
var attrs []trace.Attribute
|
||||
for k, v := range attrMap {
|
||||
var a trace.Attribute
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
a = trace.StringAttribute{k, v}
|
||||
case bool:
|
||||
a = trace.BoolAttribute{k, v}
|
||||
case int:
|
||||
a = trace.Int64Attribute{k, int64(v)}
|
||||
case int64:
|
||||
a = trace.Int64Attribute{k, v}
|
||||
default:
|
||||
a = trace.StringAttribute{k, fmt.Sprintf("%#v", v)}
|
||||
}
|
||||
attrs = append(attrs, a)
|
||||
}
|
||||
trace.FromContext(ctx).Annotatef(attrs, format, args...)
|
||||
}
|
||||
+67
-87
@@ -19,13 +19,13 @@ package testutil
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
proto3 "github.com/golang/protobuf/ptypes/struct"
|
||||
pbt "github.com/golang/protobuf/ptypes/timestamp"
|
||||
@@ -37,17 +37,14 @@ import (
|
||||
|
||||
// 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}
|
||||
Method string
|
||||
Err error
|
||||
}
|
||||
|
||||
// MockCloudSpannerClient is a mock implementation of sppb.SpannerClient.
|
||||
type MockCloudSpannerClient struct {
|
||||
sppb.SpannerClient
|
||||
|
||||
mu sync.Mutex
|
||||
t *testing.T
|
||||
// Live sessions on the client.
|
||||
@@ -62,9 +59,6 @@ type MockCloudSpannerClient struct {
|
||||
nice bool
|
||||
// Client will stall on any requests.
|
||||
freezed chan struct{}
|
||||
|
||||
// embed nil interface so updating proto with new methods don't fail the build
|
||||
sppb.SpannerClient
|
||||
}
|
||||
|
||||
// NewMockCloudSpannerClient creates new MockCloudSpannerClient instance.
|
||||
@@ -91,8 +85,8 @@ func (m *MockCloudSpannerClient) MakeStrict() {
|
||||
m.nice = false
|
||||
}
|
||||
|
||||
// InjectError injects a global error that will be returned by all APIs regardless of
|
||||
// the actions array.
|
||||
// InjectError injects a global error that will be returned by all calls to method
|
||||
// regardless of the actions array.
|
||||
func (m *MockCloudSpannerClient) InjectError(method string, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
@@ -103,7 +97,7 @@ func (m *MockCloudSpannerClient) InjectError(method string, err error) {
|
||||
func (m *MockCloudSpannerClient) SetActions(acts ...Action) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.actions = []Action{}
|
||||
m.actions = nil
|
||||
for _, act := range acts {
|
||||
m.actions = append(m.actions, act)
|
||||
}
|
||||
@@ -178,28 +172,15 @@ func (m *MockCloudSpannerClient) DeleteSession(c context.Context, r *sppb.Delete
|
||||
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) {
|
||||
m.ready()
|
||||
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.ready()
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if err := m.injErr["ExecuteStreamingSql"]; err != nil {
|
||||
act, err := m.expectAction("ExecuteStreamingSql")
|
||||
if 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{
|
||||
@@ -222,38 +203,24 @@ func (m *MockCloudSpannerClient) ExecuteStreamingSql(c context.Context, r *sppb.
|
||||
},
|
||||
ParamTypes: map[string]*sppb.Type{"var1": &sppb.Type{Code: sppb.TypeCode_STRING}},
|
||||
}
|
||||
if !reflect.DeepEqual(r, wantReq) {
|
||||
if !proto.Equal(r, wantReq) {
|
||||
return nil, fmt.Errorf("got query request: %v, want: %v", r, wantReq)
|
||||
}
|
||||
if act.err != nil {
|
||||
return nil, act.err
|
||||
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.ready()
|
||||
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.ready()
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if err := m.injErr["StreamingRead"]; err != nil {
|
||||
act, err := m.expectAction("StreamingRead", "StreamingReadIndex")
|
||||
if 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{
|
||||
@@ -284,14 +251,14 @@ func (m *MockCloudSpannerClient) StreamingRead(c context.Context, r *sppb.ReadRe
|
||||
All: false,
|
||||
},
|
||||
}
|
||||
if act.method == "StreamingIndexRead" {
|
||||
if act.Method == "StreamingIndexRead" {
|
||||
wantReq.Index = "idx1"
|
||||
}
|
||||
if !reflect.DeepEqual(r, wantReq) {
|
||||
if !proto.Equal(r, wantReq) {
|
||||
return nil, fmt.Errorf("got query request: %v, want: %v", r, wantReq)
|
||||
}
|
||||
if act.err != nil {
|
||||
return nil, act.err
|
||||
if act.Err != nil {
|
||||
return nil, act.Err
|
||||
}
|
||||
return nil, errors.New("read never succeeds on mock client")
|
||||
}
|
||||
@@ -302,19 +269,12 @@ func (m *MockCloudSpannerClient) BeginTransaction(c context.Context, r *sppb.Beg
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if !m.nice {
|
||||
if err := m.injErr["BeginTransaction"]; err != nil {
|
||||
act, err := m.expectAction("BeginTransaction")
|
||||
if 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
|
||||
if act.Err != nil {
|
||||
return nil, act.Err
|
||||
}
|
||||
}
|
||||
resp := &sppb.Transaction{Id: []byte("transaction-1")}
|
||||
@@ -330,19 +290,12 @@ func (m *MockCloudSpannerClient) Commit(c context.Context, r *sppb.CommitRequest
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if !m.nice {
|
||||
if err := m.injErr["Commit"]; err != nil {
|
||||
act, err := m.expectAction("Commit")
|
||||
if 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
|
||||
if act.Err != nil {
|
||||
return nil, act.Err
|
||||
}
|
||||
}
|
||||
return &sppb.CommitResponse{CommitTimestamp: &pbt.Timestamp{Seconds: 1, Nanos: 2}}, nil
|
||||
@@ -354,37 +307,64 @@ func (m *MockCloudSpannerClient) Rollback(c context.Context, r *sppb.RollbackReq
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if !m.nice {
|
||||
if err := m.injErr["Rollback"]; err != nil {
|
||||
act, err := m.expectAction("Rollback")
|
||||
if 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
|
||||
if act.Err != nil {
|
||||
return nil, act.Err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockCloudSpannerClient) expectAction(methods ...string) (Action, error) {
|
||||
for _, me := range methods {
|
||||
if err := m.injErr[me]; err != nil {
|
||||
return Action{}, err
|
||||
}
|
||||
}
|
||||
if len(m.actions) == 0 {
|
||||
m.t.Fatalf("unexpected %v executed", methods)
|
||||
}
|
||||
act := m.actions[0]
|
||||
m.actions = m.actions[1:]
|
||||
for _, me := range methods {
|
||||
if me == act.Method {
|
||||
return act, nil
|
||||
}
|
||||
}
|
||||
m.t.Fatalf("unexpected call of one of %v, want method %s", methods, act.Method)
|
||||
return Action{}, nil
|
||||
}
|
||||
|
||||
// Freeze stalls all requests.
|
||||
func (m *MockCloudSpannerClient) Freeze() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.freezed = make(chan struct{})
|
||||
}
|
||||
|
||||
// Unfreeze restores processing requests.
|
||||
func (m *MockCloudSpannerClient) Unfreeze() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
close(m.freezed)
|
||||
}
|
||||
|
||||
// CheckActionsConsumed checks that all actions have been consumed.
|
||||
func (m *MockCloudSpannerClient) CheckActionsConsumed() {
|
||||
if len(m.actions) != 0 {
|
||||
m.t.Fatalf("unconsumed mock client actions: %v", m.actions)
|
||||
}
|
||||
}
|
||||
|
||||
// ready checks conditions before executing requests
|
||||
// TODO: also check injected errors, actions
|
||||
// TODO: add checks for injected errors, actions
|
||||
func (m *MockCloudSpannerClient) ready() {
|
||||
m.mu.Lock()
|
||||
freezed := m.freezed
|
||||
m.mu.Unlock()
|
||||
// check if client should be freezed
|
||||
<-m.freezed
|
||||
<-freezed
|
||||
}
|
||||
|
||||
+2
-3
@@ -67,15 +67,14 @@ type MockCtlMsg struct {
|
||||
// MockCloudSpanner is a mock implementation of SpannerServer interface.
|
||||
// TODO: make MockCloudSpanner a full-fleged Cloud Spanner implementation.
|
||||
type MockCloudSpanner struct {
|
||||
sppb.SpannerServer
|
||||
|
||||
s *grpc.Server
|
||||
t *testing.T
|
||||
addr string
|
||||
msgs chan MockCtlMsg
|
||||
readTs time.Time
|
||||
next int
|
||||
|
||||
// embed nil interface so updating proto with new methods don't fail the build
|
||||
sppb.SpannerServer
|
||||
}
|
||||
|
||||
// Addr returns the listening address of mock server.
|
||||
|
||||
+4
-5
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package spanner
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -193,7 +192,7 @@ func TestKey(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%v.proto() returns error %v; want nil error", test.k, err)
|
||||
}
|
||||
if !reflect.DeepEqual(gotProto, test.wantProto) {
|
||||
if !testEqual(gotProto, test.wantProto) {
|
||||
t.Errorf("%v.proto() = \n%v\nwant:\n%v", test.k, gotProto, test.wantProto)
|
||||
}
|
||||
}
|
||||
@@ -246,7 +245,7 @@ func TestKeyRange(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%v.proto() returns error %v; want nil error", test.kr, err)
|
||||
}
|
||||
if !reflect.DeepEqual(gotProto, test.wantProto) {
|
||||
if !testEqual(gotProto, test.wantProto) {
|
||||
t.Errorf("%v.proto() = \n%v\nwant:\n%v", test.kr, gotProto.String(), test.wantProto.String())
|
||||
}
|
||||
}
|
||||
@@ -255,7 +254,7 @@ func TestKeyRange(t *testing.T) {
|
||||
func TestPrefixRange(t *testing.T) {
|
||||
got := Key{1}.AsPrefix()
|
||||
want := KeyRange{Start: Key{1}, End: Key{1}, Kind: ClosedClosed}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -366,7 +365,7 @@ func TestKeySets(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("#%d: %v.proto() returns error %v; want nil error", i, test.ks, err)
|
||||
}
|
||||
if !reflect.DeepEqual(gotProto, test.wantProto) {
|
||||
if !testEqual(gotProto, test.wantProto) {
|
||||
t.Errorf("#%d: %v.proto() = \n%v\nwant:\n%v", i, test.ks, gotProto.String(), test.wantProto.String())
|
||||
}
|
||||
}
|
||||
|
||||
+13
-14
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package spanner
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -123,7 +122,7 @@ func TestMutationToProto(t *testing.T) {
|
||||
},
|
||||
},
|
||||
} {
|
||||
if got, err := test.m.proto(); err != nil || !reflect.DeepEqual(got, test.want) {
|
||||
if got, err := test.m.proto(); err != nil || !testEqual(got, test.want) {
|
||||
t.Errorf("%d: (%#v).proto() = (%v, %v), want (%v, nil)", i, test.m, got, err, test.want)
|
||||
}
|
||||
}
|
||||
@@ -173,7 +172,7 @@ func mutationEqual(t *testing.T, m1, m2 Mutation) bool {
|
||||
ms2 := newMutationColumnSorter(&m2)
|
||||
sort.Sort(ms1)
|
||||
sort.Sort(ms2)
|
||||
return reflect.DeepEqual(ms1, ms2)
|
||||
return testEqual(ms1, ms2)
|
||||
}
|
||||
|
||||
// Test helper functions which help to generate spanner.Mutation.
|
||||
@@ -316,16 +315,16 @@ func TestMutationHelpers(t *testing.T) {
|
||||
func TestBadStructs(t *testing.T) {
|
||||
val := "i_am_not_a_struct"
|
||||
wantErr := errNotStruct(val)
|
||||
if _, gotErr := InsertStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if _, gotErr := InsertStruct("t_test", val); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("InsertStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
|
||||
}
|
||||
if _, gotErr := InsertOrUpdateStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if _, gotErr := InsertOrUpdateStruct("t_test", val); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("InsertOrUpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
|
||||
}
|
||||
if _, gotErr := UpdateStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if _, gotErr := UpdateStruct("t_test", val); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("UpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
|
||||
}
|
||||
if _, gotErr := ReplaceStruct("t_test", val); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if _, gotErr := ReplaceStruct("t_test", val); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("ReplaceStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
|
||||
}
|
||||
}
|
||||
@@ -346,13 +345,13 @@ func TestStructToMutationParams(t *testing.T) {
|
||||
{&S{F: 1}, []string{"F"}, []interface{}{1}, nil},
|
||||
} {
|
||||
gotCols, gotVals, gotErr := structToMutationParams(test.in)
|
||||
if !reflect.DeepEqual(gotCols, test.wantCols) {
|
||||
if !testEqual(gotCols, test.wantCols) {
|
||||
t.Errorf("%#v: got cols %v, want %v", test.in, gotCols, test.wantCols)
|
||||
}
|
||||
if !reflect.DeepEqual(gotVals, test.wantVals) {
|
||||
if !testEqual(gotVals, test.wantVals) {
|
||||
t.Errorf("%#v: got vals %v, want %v", test.in, gotVals, test.wantVals)
|
||||
}
|
||||
if !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if !testEqual(gotErr, test.wantErr) {
|
||||
t.Errorf("%#v: got err %v, want %v", test.in, gotErr, test.wantErr)
|
||||
}
|
||||
}
|
||||
@@ -499,12 +498,12 @@ func TestEncodeMutation(t *testing.T) {
|
||||
} {
|
||||
gotProto, gotErr := test.mutation.proto()
|
||||
if gotErr != nil {
|
||||
if !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if !testEqual(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) {
|
||||
if !testEqual(gotProto, test.wantProto) {
|
||||
t.Errorf("%s: %v.proto() = (%v, nil), want (%v, nil)", test.name, test.mutation, gotProto, test.wantProto)
|
||||
}
|
||||
}
|
||||
@@ -559,12 +558,12 @@ func TestEncodeMutationArray(t *testing.T) {
|
||||
} {
|
||||
gotProto, gotErr := mutationsProto(test.ms)
|
||||
if gotErr != nil {
|
||||
if !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if !testEqual(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) {
|
||||
if !testEqual(gotProto, test.want) {
|
||||
t.Errorf("%v: mutationsProto(%v) = (%v, nil), want (%v, nil)", test.name, test.ms, gotProto, test.want)
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package spanner
|
||||
|
||||
// numChannels is the default value for NumChannels of client
|
||||
const numChannels = 4
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
// +build !go1.8
|
||||
|
||||
package spanner
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
// OpenCensus only supports go 1.8 and higher.
|
||||
|
||||
func openCensusOptions() []option.ClientOption { return nil }
|
||||
|
||||
func traceStartSpan(ctx context.Context, _ string) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func traceEndSpan(context.Context, error) {
|
||||
}
|
||||
|
||||
func tracePrintf(context.Context, map[string]interface{}, string, ...interface{}) {
|
||||
}
|
||||
+24
-5
@@ -19,14 +19,14 @@ package spanner
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"cloud.google.com/go/internal/protostruct"
|
||||
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"
|
||||
@@ -47,6 +47,7 @@ func errEarlyReadEnd() error {
|
||||
// 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)
|
||||
ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.RowIterator")
|
||||
return &RowIterator{
|
||||
streamd: newResumableStreamDecoder(ctx, rpc),
|
||||
rowd: &partialResultSetDecoder{},
|
||||
@@ -58,6 +59,14 @@ func stream(ctx context.Context, rpc func(ct context.Context, resumeToken []byte
|
||||
|
||||
// RowIterator is an iterator over Rows.
|
||||
type RowIterator struct {
|
||||
// The plan for the query. Available after RowIterator.Next returns iterator.Done
|
||||
// if QueryWithStats was called.
|
||||
QueryPlan *sppb.QueryPlan
|
||||
|
||||
// Execution statistics for the query. Available after RowIterator.Next returns iterator.Done
|
||||
// if QueryWithStats was called.
|
||||
QueryStats map[string]interface{}
|
||||
|
||||
streamd *resumableStreamDecoder
|
||||
rowd *partialResultSetDecoder
|
||||
setTimestamp func(time.Time)
|
||||
@@ -75,7 +84,12 @@ func (r *RowIterator) Next() (*Row, error) {
|
||||
return nil, r.err
|
||||
}
|
||||
for len(r.rows) == 0 && r.streamd.next() {
|
||||
r.rows, r.err = r.rowd.add(r.streamd.get())
|
||||
prs := r.streamd.get()
|
||||
if prs.Stats != nil {
|
||||
r.QueryPlan = prs.Stats.QueryPlan
|
||||
r.QueryStats = protostruct.DecodeToMap(prs.Stats.QueryStats)
|
||||
}
|
||||
r.rows, r.err = r.rowd.add(prs)
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
@@ -125,6 +139,9 @@ func (r *RowIterator) Do(f func(r *Row) error) error {
|
||||
|
||||
// Stop terminates the iteration. It should be called after every iteration.
|
||||
func (r *RowIterator) Stop() {
|
||||
if r.streamd != nil {
|
||||
defer traceEndSpan(r.streamd.ctx, r.err)
|
||||
}
|
||||
if r.cancel != nil {
|
||||
r.cancel()
|
||||
}
|
||||
@@ -441,7 +458,7 @@ func (d *resumableStreamDecoder) next() bool {
|
||||
return true
|
||||
|
||||
default:
|
||||
log.Errorf("Unexpected resumableStreamDecoder.state: %v", d.state)
|
||||
log.Printf("Unexpected resumableStreamDecoder.state: %v", d.state)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -486,7 +503,9 @@ func (d *resumableStreamDecoder) resetBackOff() {
|
||||
|
||||
// doBackoff does an exponential backoff sleep.
|
||||
func (d *resumableStreamDecoder) doBackOff() {
|
||||
ticker := time.NewTicker(d.backoff.delay(d.retryCount))
|
||||
delay := d.backoff.delay(d.retryCount)
|
||||
tracePrintf(d.ctx, nil, "Backing off stream read for %s", delay)
|
||||
ticker := time.NewTicker(delay)
|
||||
defer ticker.Stop()
|
||||
d.retryCount++
|
||||
select {
|
||||
|
||||
+48
-19
@@ -20,7 +20,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -588,10 +587,10 @@ nextTest:
|
||||
}
|
||||
rows = append(rows, rs...)
|
||||
}
|
||||
if !reflect.DeepEqual(p.ts, test.wantTs) {
|
||||
if !testEqual(p.ts, test.wantTs) {
|
||||
t.Errorf("got transaction(%v), want %v", p.ts, test.wantTs)
|
||||
}
|
||||
if !reflect.DeepEqual(rows, test.wantF) {
|
||||
if !testEqual(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 {
|
||||
@@ -840,13 +839,13 @@ nextTest:
|
||||
if stateDone {
|
||||
// Check if resumableStreamDecoder carried out expected
|
||||
// state transitions.
|
||||
if !reflect.DeepEqual(st, test.stateHistory) {
|
||||
if !testEqual(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) {
|
||||
if !testEqual(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.
|
||||
@@ -858,15 +857,15 @@ nextTest:
|
||||
}
|
||||
q = append(q, item)
|
||||
}
|
||||
if !reflect.DeepEqual(q, test.queue) {
|
||||
if !testEqual(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) {
|
||||
if test.resumeToken != nil && !testEqual(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) {
|
||||
if !testEqual(lastErr, test.wantErr) {
|
||||
t.Errorf("%v: got error %v, want %v", test.name, lastErr, test.wantErr)
|
||||
}
|
||||
// Proceed to next test
|
||||
@@ -1120,25 +1119,25 @@ func TestRsdBlockingStates(t *testing.T) {
|
||||
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) {
|
||||
if !testEqual(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) {
|
||||
if !testEqual(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) {
|
||||
if !testEqual(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) {
|
||||
if test.resumeToken != nil && !testEqual(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) {
|
||||
if !testEqual(lastErr, test.wantErr) {
|
||||
t.Errorf("%v: got error %v, want %v", test.name, lastErr, test.wantErr)
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
@@ -1340,7 +1339,7 @@ func TestResumeToken(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rows, want) {
|
||||
if !testEqual(rows, want) {
|
||||
t.Errorf("received rows: \n%v\n; but want\n%v\n", rows, want)
|
||||
}
|
||||
// Inject resumable failure.
|
||||
@@ -1365,7 +1364,7 @@ func TestResumeToken(t *testing.T) {
|
||||
{Kind: &proto3.Value_StringValue{StringValue: valStr(2)}},
|
||||
},
|
||||
})
|
||||
if !reflect.DeepEqual(rows, want) {
|
||||
if !testEqual(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.
|
||||
@@ -1376,7 +1375,7 @@ func TestResumeToken(t *testing.T) {
|
||||
t.Fatalf("failed to wait for row 3-%v: %v", maxBuffers+1, err)
|
||||
}
|
||||
// Received rows should be unchanged.
|
||||
if !reflect.DeepEqual(rows, want) {
|
||||
if !testEqual(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:
|
||||
@@ -1396,7 +1395,7 @@ func TestResumeToken(t *testing.T) {
|
||||
},
|
||||
})
|
||||
}
|
||||
if !reflect.DeepEqual(rows, want) {
|
||||
if !testEqual(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
|
||||
@@ -1411,7 +1410,7 @@ func TestResumeToken(t *testing.T) {
|
||||
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(gotErr, wantErr) {
|
||||
if wantErr := toSpannerError(grpc.Errorf(codes.Unavailable, "mock server wants some sleep")); !testEqual(gotErr, wantErr) {
|
||||
t.Fatalf("stream() returns error: %v, but want error: %v", gotErr, wantErr)
|
||||
}
|
||||
|
||||
@@ -1455,7 +1454,7 @@ func TestResumeToken(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rows, want) {
|
||||
if !testEqual(rows, want) {
|
||||
t.Errorf("received rows: \n%v\n; but want\n%v\n", rows, want)
|
||||
}
|
||||
}
|
||||
@@ -1647,6 +1646,36 @@ func TestRowIteratorDo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRowIteratorDoWithError(t *testing.T) {
|
||||
restore := setMaxBytesBetweenResumeTokens()
|
||||
defer restore()
|
||||
ms := testutil.NewMockCloudSpanner(t, trxTs)
|
||||
ms.Serve()
|
||||
defer ms.Stop()
|
||||
cc := dialMock(t, ms)
|
||||
defer cc.Close()
|
||||
mc := sppb.NewSpannerClient(cc)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
ms.AddMsg(nil, false)
|
||||
}
|
||||
ms.AddMsg(io.EOF, true)
|
||||
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) {})
|
||||
injected := errors.New("Failed iterator")
|
||||
err := iter.Do(func(r *Row) error { return injected })
|
||||
if err != injected {
|
||||
t.Errorf("got <%v>, want <%v>", err, injected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIteratorStopEarly(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
restore := setMaxBytesBetweenResumeTokens()
|
||||
|
||||
+7
-1
@@ -157,6 +157,11 @@ func extractRetryDelay(err error) (time.Duration, bool) {
|
||||
// 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 {
|
||||
return toSpannerError(runRetryableNoWrap(ctx, f))
|
||||
}
|
||||
|
||||
// Like runRetryable, but doesn't wrap the returned error in a spanner.Error.
|
||||
func runRetryableNoWrap(ctx context.Context, f func(context.Context) error) error {
|
||||
var funcErr error
|
||||
retryCount := 0
|
||||
for {
|
||||
@@ -178,6 +183,7 @@ func runRetryable(ctx context.Context, f func(context.Context) error) error {
|
||||
if !ok {
|
||||
b = defaultBackoff.delay(retryCount)
|
||||
}
|
||||
tracePrintf(ctx, nil, "Backing off for %s, then retrying", b)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errContextCanceled(ctx, funcErr)
|
||||
@@ -187,6 +193,6 @@ func runRetryable(ctx context.Context, f func(context.Context) error) error {
|
||||
continue
|
||||
}
|
||||
// Error isn't retryable / no error, return immediately.
|
||||
return toSpannerError(funcErr)
|
||||
return funcErr
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -19,7 +19,6 @@ package spanner
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -60,7 +59,7 @@ func TestRetry(t *testing.T) {
|
||||
err = runRetryable(context.Background(), func(ct context.Context) error {
|
||||
return injErr
|
||||
})
|
||||
if wantErr := toSpannerError(injErr); !reflect.DeepEqual(err, wantErr) {
|
||||
if wantErr := toSpannerError(injErr); !testEqual(err, wantErr) {
|
||||
t.Errorf("runRetryable returns error %v, want %v", err, wantErr)
|
||||
}
|
||||
// Timeout
|
||||
@@ -74,7 +73,7 @@ func TestRetry(t *testing.T) {
|
||||
return retryErr
|
||||
})
|
||||
// Check error code and error message
|
||||
if wantErrCode, wantErr := codes.DeadlineExceeded, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !reflect.DeepEqual(err, wantErr) {
|
||||
if wantErrCode, wantErr := codes.DeadlineExceeded, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !testEqual(err, wantErr) {
|
||||
t.Errorf("<err code, err>=\n<%v, %v>, want:\n<%v, %v>", ErrCode(err), err, wantErrCode, wantErr)
|
||||
}
|
||||
// Cancellation
|
||||
@@ -89,7 +88,7 @@ func TestRetry(t *testing.T) {
|
||||
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 {
|
||||
if wantErrCode, wantErr := codes.Canceled, errContextCanceled(ctx, retryErr); ErrCode(err) != wantErrCode || !testEqual(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)
|
||||
}
|
||||
}
|
||||
@@ -102,7 +101,7 @@ func TestRetryInfo(t *testing.T) {
|
||||
retryInfoKey: string(b),
|
||||
}
|
||||
gotDelay, ok := extractRetryDelay(errRetry(toSpannerErrorWithMetadata(grpc.Errorf(codes.Aborted, ""), metadata.New(trailers))))
|
||||
if !ok || !reflect.DeepEqual(time.Second, gotDelay) {
|
||||
if !ok || !testEqual(time.Second, gotDelay) {
|
||||
t.Errorf("<ok, retryDelay> = <%t, %v>, want <true, %v>", ok, gotDelay, time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
+47
-20
@@ -242,7 +242,7 @@ func TestColumnValues(t *testing.T) {
|
||||
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) {
|
||||
if got := reflect.Indirect(gotp).Interface(); !testEqual(got, want) {
|
||||
t.Errorf("\t row.Column(%v, %T) retrives %v, want %v", i, gotp.Interface(), got, want)
|
||||
}
|
||||
// ColumnByName
|
||||
@@ -251,7 +251,7 @@ func TestColumnValues(t *testing.T) {
|
||||
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) {
|
||||
if got := reflect.Indirect(gotp).Interface(); !testEqual(got, want) {
|
||||
t.Errorf("\t row.ColumnByName(%v, %T) retrives %v, want %v", row.fields[i].Name, gotp.Interface(), got, want)
|
||||
}
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func TestColumnValues(t *testing.T) {
|
||||
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) {
|
||||
if got := reflect.Indirect(reflect.ValueOf(vals[i])).Interface(); !testEqual(got, want) {
|
||||
t.Errorf("\t got %v(%T) for column[%v], want %v(%T)", got, got, row.fields[i].Name, want, want)
|
||||
}
|
||||
}
|
||||
@@ -339,10 +339,10 @@ func TestNilDst(t *testing.T) {
|
||||
})(nil)),
|
||||
},
|
||||
} {
|
||||
if gotErr := test.r.Column(0, test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if gotErr := test.r.Column(0, test.dst); !testEqual(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) {
|
||||
if gotErr := test.r.ColumnByName("Col0", test.dst); !testEqual(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.
|
||||
@@ -350,10 +350,10 @@ func TestNilDst(t *testing.T) {
|
||||
if test.dst == nil {
|
||||
wantColumnsErr = nil
|
||||
}
|
||||
if gotErr := test.r.Columns(test.dst); !reflect.DeepEqual(gotErr, wantColumnsErr) {
|
||||
if gotErr := test.r.Columns(test.dst); !testEqual(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) {
|
||||
if gotErr := test.r.ToStruct(test.structDst); !testEqual(gotErr, test.wantToStructErr) {
|
||||
t.Errorf("%v: test.r.ToStruct() returns error %v, want %v", i, gotErr, test.wantToStructErr)
|
||||
}
|
||||
}
|
||||
@@ -401,7 +401,7 @@ func TestNullTypeErr(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
wantErr := errDecodeColumn(ntoi(test.colName), errDstNotForNull(test.dst))
|
||||
if gotErr := row.ColumnByName(test.colName, test.dst); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if gotErr := row.ColumnByName(test.colName, test.dst); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("row.ColumnByName(%v) returns error %v, want %v", test.colName, gotErr, wantErr)
|
||||
}
|
||||
}
|
||||
@@ -418,11 +418,11 @@ func TestColumnTypeErr(t *testing.T) {
|
||||
etc = f.Type.ArrayElementType.Code
|
||||
}
|
||||
wantErr := errDecodeColumn(i, errTypeMismatch(tc, etc, badDst))
|
||||
if gotErr := row.Column(i, badDst); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if gotErr := row.Column(i, badDst); !testEqual(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) {
|
||||
if gotErr := row.ColumnByName(f.Name, badDst); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("ColumnByName(%v): decoding into destination with wrong type %T returns error %v, want %v",
|
||||
f.Name, badDst, gotErr, wantErr)
|
||||
}
|
||||
@@ -434,7 +434,7 @@ func TestColumnTypeErr(t *testing.T) {
|
||||
for i := 2; i < len(row.fields); i++ {
|
||||
vals = append(vals, nil)
|
||||
}
|
||||
if gotErr := row.Columns(vals...); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if gotErr := row.Columns(vals...); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("Columns(): decoding column 1 with wrong type %T returns error %v, want %v",
|
||||
badDst, gotErr, wantErr)
|
||||
}
|
||||
@@ -522,7 +522,7 @@ func TestInvalidColumnRequest(t *testing.T) {
|
||||
errUnnamedField(&sppb.StructType{Fields: []*sppb.StructType_Field{{"", stringType()}}}, 0),
|
||||
},
|
||||
} {
|
||||
if gotErr := test.f(); !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if gotErr := test.f(); !testEqual(gotErr, test.wantErr) {
|
||||
t.Errorf("%v: test.f() returns error %v, want %v", test.desc, gotErr, test.wantErr)
|
||||
}
|
||||
}
|
||||
@@ -574,7 +574,7 @@ func TestToStructInvalidDst(t *testing.T) {
|
||||
errTypeMismatch(sppb.TypeCode_STRING, sppb.TypeCode_TYPE_CODE_UNSPECIFIED, proto.Int64(0))),
|
||||
},
|
||||
} {
|
||||
if gotErr := row.ToStruct(test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if gotErr := row.ToStruct(test.dst); !testEqual(gotErr, test.wantErr) {
|
||||
t.Errorf("%v: decoding:\ngot %v\nwant %v", test.desc, gotErr, test.wantErr)
|
||||
}
|
||||
}
|
||||
@@ -1560,13 +1560,13 @@ func TestBrokenRow(t *testing.T) {
|
||||
),
|
||||
},
|
||||
} {
|
||||
if gotErr := test.row.Column(0, test.dst); !reflect.DeepEqual(gotErr, test.wantErr) {
|
||||
if gotErr := test.row.Column(0, test.dst); !testEqual(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) {
|
||||
if gotErr := test.row.ColumnByName("Col0", test.dst); !testEqual(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) {
|
||||
if gotErr := test.row.Columns(test.dst); !testEqual(gotErr, test.wantErr) {
|
||||
t.Errorf("%v: test.row.Columns(%T) got error %v, want %v", i, test.dst, gotErr, test.wantErr)
|
||||
}
|
||||
}
|
||||
@@ -1684,12 +1684,39 @@ func TestToStruct(t *testing.T) {
|
||||
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]) {
|
||||
} else if !testEqual(s[0], s[1]) {
|
||||
t.Errorf("row.ToStruct() fetches struct %v, want %v", s[0], s[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestToStructEmbedded(t *testing.T) {
|
||||
type (
|
||||
S1 struct{ F1 string }
|
||||
S2 struct {
|
||||
S1
|
||||
F2 string
|
||||
}
|
||||
)
|
||||
r := Row{
|
||||
[]*sppb.StructType_Field{
|
||||
{"F1", stringType()},
|
||||
{"F2", stringType()},
|
||||
},
|
||||
[]*proto3.Value{
|
||||
stringProto("v1"),
|
||||
stringProto("v2"),
|
||||
},
|
||||
}
|
||||
var got S2
|
||||
if err := r.ToStruct(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := S2{S1: S1{F1: "v1"}, F2: "v2"}
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("got %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test helpers for getting column names.
|
||||
func TestColumnNameAndIndex(t *testing.T) {
|
||||
// Test Row.Size().
|
||||
@@ -1763,11 +1790,11 @@ func TestNewRow(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
got, err := NewRow(test.names, test.values)
|
||||
if !reflect.DeepEqual(err, test.wantErr) {
|
||||
if !testEqual(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) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("NewRow(%v,%v) = %s, want %s", test.names, test.values, got, test.want)
|
||||
continue
|
||||
}
|
||||
|
||||
+53
-104
@@ -20,12 +20,12 @@ import (
|
||||
"container/heap"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
sppb "google.golang.org/genproto/googleapis/spanner/v1"
|
||||
@@ -171,60 +171,6 @@ func (s *session) ping() error {
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
s.createTime = time.Now()
|
||||
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()
|
||||
@@ -320,8 +266,8 @@ func (s *session) destroy(isExpire bool) bool {
|
||||
_, 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)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete session %v. Error: %v", s.getID(), err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -352,9 +298,6 @@ type SessionPoolConfig struct {
|
||||
// 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
|
||||
// MaxIdle is the maximum number of idle sessions, pool is allowed to keep. Defaults to 0.
|
||||
MaxIdle uint64
|
||||
// MaxBurst is the maximum number of concurrent session creation requests. Defaults to 10.
|
||||
@@ -365,8 +308,6 @@ type SessionPoolConfig struct {
|
||||
HealthCheckWorkers int
|
||||
// HealthCheckInterval is how often the health checker pings a session. Defaults to 5 min.
|
||||
HealthCheckInterval time.Duration
|
||||
// healthCheckMaintainerEnabled enables the session pool maintainer.
|
||||
healthCheckMaintainerEnabled bool
|
||||
// healthCheckSampleInterval is how often the health checker samples live session (for use in maintaining session pool size). Defaults to 1 min.
|
||||
healthCheckSampleInterval time.Duration
|
||||
}
|
||||
@@ -502,6 +443,7 @@ func (p *sessionPool) shouldPrepareWrite() bool {
|
||||
}
|
||||
|
||||
func (p *sessionPool) createSession(ctx context.Context) (*session, error) {
|
||||
tracePrintf(ctx, nil, "Creating a new session")
|
||||
doneCreate := func(done bool) {
|
||||
p.mu.Lock()
|
||||
if !done {
|
||||
@@ -555,6 +497,7 @@ func (p *sessionPool) isHealthy(s *session) bool {
|
||||
// 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) {
|
||||
tracePrintf(ctx, nil, "Acquiring a read-only session")
|
||||
ctx = contextWithOutgoingMetadata(ctx, p.md)
|
||||
for {
|
||||
var (
|
||||
@@ -570,8 +513,12 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
|
||||
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)
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
|
||||
"Acquired read-only session")
|
||||
} else if p.idleWriteList.Len() > 0 {
|
||||
s = p.idleWriteList.Remove(p.idleWriteList.Front()).(*session)
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
|
||||
"Acquired read-write session")
|
||||
}
|
||||
if s != nil {
|
||||
s.setIdleList(nil)
|
||||
@@ -588,8 +535,10 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
|
||||
if (p.MaxOpened > 0 && p.numOpened >= p.MaxOpened) || (p.MaxBurst > 0 && p.createReqs >= p.MaxBurst) {
|
||||
mayGetSession := p.mayGetSession
|
||||
p.mu.Unlock()
|
||||
tracePrintf(ctx, nil, "Waiting for read-only session to become available")
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
tracePrintf(ctx, nil, "Context done waiting for session")
|
||||
return nil, errGetSessionTimeout()
|
||||
case <-mayGetSession:
|
||||
}
|
||||
@@ -600,8 +549,11 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
|
||||
p.createReqs++
|
||||
p.mu.Unlock()
|
||||
if s, err = p.createSession(ctx); err != nil {
|
||||
tracePrintf(ctx, nil, "Error creating session: %v", err)
|
||||
return nil, toSpannerError(err)
|
||||
}
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
|
||||
"Created session")
|
||||
return &sessionHandle{session: s}, nil
|
||||
}
|
||||
}
|
||||
@@ -609,6 +561,7 @@ func (p *sessionPool) take(ctx context.Context) (*sessionHandle, error) {
|
||||
// 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) {
|
||||
tracePrintf(ctx, nil, "Acquiring a read-write session")
|
||||
ctx = contextWithOutgoingMetadata(ctx, p.md)
|
||||
for {
|
||||
var (
|
||||
@@ -624,8 +577,10 @@ func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, err
|
||||
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)
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, "Acquired read-write session")
|
||||
} else if p.idleList.Len() > 0 {
|
||||
s = p.idleList.Remove(p.idleList.Front()).(*session)
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()}, "Acquired read-only session")
|
||||
}
|
||||
if s != nil {
|
||||
s.setIdleList(nil)
|
||||
@@ -636,34 +591,39 @@ func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, err
|
||||
if !p.isHealthy(s) {
|
||||
continue
|
||||
}
|
||||
if !s.isWritePrepared() {
|
||||
if err = s.prepareForWrite(ctx); err != nil {
|
||||
return nil, toSpannerError(err)
|
||||
} else {
|
||||
// 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()
|
||||
tracePrintf(ctx, nil, "Waiting for read-write session to become available")
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
tracePrintf(ctx, nil, "Context done waiting for session")
|
||||
return nil, errGetSessionTimeout()
|
||||
case <-mayGetSession:
|
||||
}
|
||||
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)
|
||||
// Take budget before the actual session creation.
|
||||
p.numOpened++
|
||||
p.createReqs++
|
||||
p.mu.Unlock()
|
||||
if s, err = p.createSession(ctx); err != nil {
|
||||
tracePrintf(ctx, nil, "Error creating session: %v", err)
|
||||
return nil, toSpannerError(err)
|
||||
}
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
|
||||
"Created session")
|
||||
}
|
||||
if err = s.prepareForWrite(ctx); err != nil {
|
||||
return nil, toSpannerError(err)
|
||||
if !s.isWritePrepared() {
|
||||
if err = s.prepareForWrite(ctx); err != nil {
|
||||
s.recycle()
|
||||
tracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
|
||||
"Error preparing session for write")
|
||||
return nil, toSpannerError(err)
|
||||
}
|
||||
}
|
||||
return &sessionHandle{session: s}, nil
|
||||
}
|
||||
@@ -677,10 +637,6 @@ func (p *sessionPool) recycle(s *session) bool {
|
||||
// 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
|
||||
}
|
||||
// Put session at the back of the list to round robin for load balancing across channels.
|
||||
if s.isWritePrepared() {
|
||||
s.setIdleList(p.idleWriteList.PushBack(s))
|
||||
@@ -796,10 +752,8 @@ func newHealthChecker(interval time.Duration, workers int, sampleInterval time.D
|
||||
ready: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
if hc.pool.healthCheckMaintainerEnabled {
|
||||
hc.waitWorkers.Add(1)
|
||||
go hc.maintainer()
|
||||
}
|
||||
hc.waitWorkers.Add(1)
|
||||
go hc.maintainer()
|
||||
for i := 1; i <= hc.workers; i++ {
|
||||
hc.waitWorkers.Add(1)
|
||||
go hc.worker(i)
|
||||
@@ -881,12 +835,6 @@ func (hc *healthChecker) healthCheck(s *session) {
|
||||
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)
|
||||
@@ -951,8 +899,8 @@ func (hc *healthChecker) worker(i int) {
|
||||
err := ws.prepareForWrite(contextWithOutgoingMetadata(ctx, hc.pool.md))
|
||||
cancel()
|
||||
if err != nil {
|
||||
// TODO(dixiao): handle error properly
|
||||
log.Errorf("prepareForWrite failed: %v", err)
|
||||
// Skip handling prepare error, session can be prepared in next cycle
|
||||
log.Printf("Failed to prepare session, error: %v", toSpannerError(err))
|
||||
}
|
||||
hc.pool.recycle(ws)
|
||||
hc.pool.mu.Lock()
|
||||
@@ -1021,12 +969,13 @@ func (hc *healthChecker) maintainer() {
|
||||
err error
|
||||
)
|
||||
if s, err = p.createSession(ctx); err != nil {
|
||||
log.Warningf("Failed to create session, error: %v", toSpannerError(err))
|
||||
log.Printf("Failed to create session, error: %v", toSpannerError(err))
|
||||
continue
|
||||
}
|
||||
if shouldPrepareWrite {
|
||||
if err = s.prepareForWrite(ctx); err != nil {
|
||||
log.Warningf("Failed to prepare session, error: %v", toSpannerError(err))
|
||||
p.recycle(s)
|
||||
log.Printf("Failed to prepare session, error: %v", toSpannerError(err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
+45
-89
@@ -17,9 +17,9 @@ limitations under the License.
|
||||
package spanner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/heap"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -44,7 +44,6 @@ func setup(t *testing.T, spc SessionPoolConfig) (sp *sessionPool, sc *testutil.M
|
||||
if spc.healthCheckSampleInterval == 0 {
|
||||
spc.healthCheckSampleInterval = 10 * time.Millisecond
|
||||
}
|
||||
spc.healthCheckMaintainerEnabled = true
|
||||
sp, err := newSessionPool("mockdb", spc, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create session pool: %v", err)
|
||||
@@ -75,7 +74,7 @@ func TestSessionCreation(t *testing.T) {
|
||||
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) {
|
||||
if wantDs := sc.DumpSessions(); !testEqual(gotDs, wantDs) {
|
||||
t.Errorf("session pool creates sessions %v, want %v", gotDs, wantDs)
|
||||
}
|
||||
// Verify that created sessions are recorded correctly in session pool.
|
||||
@@ -134,7 +133,7 @@ func TestTakeFromIdleList(t *testing.T) {
|
||||
if len(gotSessions) != 10 {
|
||||
t.Errorf("got %v unique sessions, want 10", len(gotSessions))
|
||||
}
|
||||
if !reflect.DeepEqual(gotSessions, wantSessions) {
|
||||
if !testEqual(gotSessions, wantSessions) {
|
||||
t.Errorf("got sessions: %v, want %v", gotSessions, wantSessions)
|
||||
}
|
||||
}
|
||||
@@ -144,10 +143,10 @@ func TestTakeWriteSessionFromIdleList(t *testing.T) {
|
||||
t.Parallel()
|
||||
sp, sc, cancel := setup(t, SessionPoolConfig{MaxIdle: 20}) // make sure maintainer keeps the idle sessions
|
||||
defer cancel()
|
||||
act := testutil.NewAction("Begin", nil)
|
||||
|
||||
acts := make([]testutil.Action, 20)
|
||||
for i := 0; i < len(acts); i++ {
|
||||
acts[i] = act
|
||||
acts[i] = testutil.Action{"BeginTransaction", nil}
|
||||
}
|
||||
sc.SetActions(acts...)
|
||||
// Take ten sessions from session pool and recycle them.
|
||||
@@ -178,7 +177,7 @@ func TestTakeWriteSessionFromIdleList(t *testing.T) {
|
||||
if len(gotSessions) != 10 {
|
||||
t.Errorf("got %v unique sessions, want 10", len(gotSessions))
|
||||
}
|
||||
if !reflect.DeepEqual(gotSessions, wantSessions) {
|
||||
if !testEqual(gotSessions, wantSessions) {
|
||||
t.Errorf("got sessions: %v, want %v", gotSessions, wantSessions)
|
||||
}
|
||||
}
|
||||
@@ -216,7 +215,7 @@ func TestTakeFromIdleListChecked(t *testing.T) {
|
||||
}
|
||||
// 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) {
|
||||
if got, want := sc.DumpPings(), ([]string{wantSid}); !testEqual(got, want) {
|
||||
t.Errorf("%v - got ping session requests: %v, want %v", i, got, want)
|
||||
}
|
||||
sh.recycle()
|
||||
@@ -273,7 +272,7 @@ func TestTakeFromIdleWriteListChecked(t *testing.T) {
|
||||
}
|
||||
// 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) {
|
||||
if got, want := sc.DumpPings(), ([]string{wantSid}); !testEqual(got, want) {
|
||||
t.Errorf("%v - got ping session requests: %v, want %v", i, got, want)
|
||||
}
|
||||
sh.recycle()
|
||||
@@ -312,7 +311,7 @@ func TestMaxOpenedSessions(t *testing.T) {
|
||||
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) {
|
||||
if wantErr := errGetSessionTimeout(); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("the second session retrival returns error %v, want %v", gotErr, wantErr)
|
||||
}
|
||||
go func() {
|
||||
@@ -389,7 +388,7 @@ func TestMaxBurst(t *testing.T) {
|
||||
defer cancel()
|
||||
sh, gotErr := sp.take(ctx)
|
||||
// Since MaxBurst == 1, the second session request should block.
|
||||
if wantErr := errGetSessionTimeout(); !reflect.DeepEqual(gotErr, wantErr) {
|
||||
if wantErr := errGetSessionTimeout(); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("session retrival returns error %v, want %v", gotErr, wantErr)
|
||||
}
|
||||
// Let the first session request succeed.
|
||||
@@ -410,10 +409,8 @@ func TestSessionRecycle(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
sp, _, cancel := setup(t, SessionPoolConfig{maxSessionAge: 100 * time.Millisecond, MinOpened: 1, MaxIdle: 2})
|
||||
sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1, MaxIdle: 2})
|
||||
// Set MaxIdle to ensure shs[0] is not destroyed from scale down.
|
||||
// Healthcheck is explicitly turned off in this test because it might aggressively expire sessions in idle list.
|
||||
sp.hc.close()
|
||||
defer cancel()
|
||||
|
||||
// Test session is correctly recycled and reused.
|
||||
@@ -427,31 +424,6 @@ func TestSessionRecycle(t *testing.T) {
|
||||
if sp.numOpened != 1 {
|
||||
t.Errorf("Expect session pool size %d, got %d", 1, sp.numOpened)
|
||||
}
|
||||
|
||||
// Test recycling expired session.
|
||||
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.
|
||||
@@ -459,6 +431,7 @@ func TestSessionDestroy(t *testing.T) {
|
||||
t.Parallel()
|
||||
sp, _, cancel := setup(t, SessionPoolConfig{MinOpened: 1})
|
||||
defer cancel()
|
||||
<-time.After(10 * time.Millisecond) // maintainer will create one session, we wait for it create session to avoid flakiness in test
|
||||
sh, err := sp.take(context.Background())
|
||||
if err != nil {
|
||||
t.Errorf("cannot get session from session pool: %v", err)
|
||||
@@ -467,11 +440,11 @@ func TestSessionDestroy(t *testing.T) {
|
||||
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)
|
||||
t.Errorf("session %v invalid, want it to stay alive. (destroy in expiration mode, success: %v)", s, d)
|
||||
}
|
||||
if d := s.destroy(false); !d || s.isValid() {
|
||||
// Session should be destroyed.
|
||||
t.Errorf("failed to destroy session %s", s)
|
||||
t.Errorf("failed to destroy session %v. (destroy in default mode, success: %v)", s, d)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +474,7 @@ func TestHcHeap(t *testing.T) {
|
||||
for idx := 0; hh.Len() > 0; idx++ {
|
||||
got := heap.Pop(&hh).(*session)
|
||||
want[idx].hcIndex = -1
|
||||
if !reflect.DeepEqual(got, want[idx]) {
|
||||
if !testEqual(got, want[idx]) {
|
||||
t.Errorf("%v: heap.Pop returns %v, want %v", idx, got, want[idx])
|
||||
}
|
||||
}
|
||||
@@ -629,7 +602,7 @@ func TestSessionHealthCheck(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
}
|
||||
sp, sc, cancel := setup(t, SessionPoolConfig{maxSessionAge: 2 * time.Second})
|
||||
sp, sc, cancel := setup(t, SessionPoolConfig{})
|
||||
defer cancel()
|
||||
// Test pinging sessions.
|
||||
sh, err := sp.take(context.Background())
|
||||
@@ -641,14 +614,6 @@ func TestSessionHealthCheck(t *testing.T) {
|
||||
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 {
|
||||
@@ -657,6 +622,7 @@ func TestSessionHealthCheck(t *testing.T) {
|
||||
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)
|
||||
s := sh.session
|
||||
if sh.session.isValid() {
|
||||
t.Errorf("session(%v) is still alive, want it to be dropped by healthcheck workers", s)
|
||||
}
|
||||
@@ -670,43 +636,20 @@ func TestSessionHealthCheck(t *testing.T) {
|
||||
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.
|
||||
// 4) Healthcheck destroys a broken session (because a worker has already destroyed it).
|
||||
// 5) 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.
|
||||
// During the test, the session pool maintainer maintains the number of sessions,
|
||||
// and it is expected that all sessions that are taken from session pool remains valid.
|
||||
// When all test workers and healthcheck workers exit, mockclient, session pool
|
||||
// and healthchecker should be in consistent state.
|
||||
|
||||
func TestStressSessionPool(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Use concurrent workers to test different session pool built from different configurations.
|
||||
@@ -715,11 +658,10 @@ func TestStressSessionPool(t *testing.T) {
|
||||
}
|
||||
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},
|
||||
SessionPoolConfig{MinOpened: 10, MaxOpened: 200, MaxBurst: 5},
|
||||
SessionPoolConfig{MinOpened: 10, MaxOpened: 200, MaxBurst: 5, WriteSessions: 0.2},
|
||||
} {
|
||||
var wg sync.WaitGroup
|
||||
// Create a more aggressive session healthchecker to increase test concurrency.
|
||||
@@ -758,7 +700,7 @@ func TestStressSessionPool(t *testing.T) {
|
||||
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) {
|
||||
if wantErr := errInvalidSessionPool(); !testEqual(gotErr, wantErr) {
|
||||
t.Errorf("%v.%v: got error when pool is closed: %v, want %v", ti, idx, gotErr, wantErr)
|
||||
}
|
||||
continue
|
||||
@@ -771,9 +713,9 @@ func TestStressSessionPool(t *testing.T) {
|
||||
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 {
|
||||
if 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))))
|
||||
<-time.After(time.Duration(rand.Int63n(int64(cfg.HealthCheckInterval))))
|
||||
}
|
||||
if rand.Intn(100) < idx {
|
||||
// destroy the session.
|
||||
@@ -823,10 +765,10 @@ func TestStressSessionPool(t *testing.T) {
|
||||
sp.mu.Unlock()
|
||||
|
||||
// Verify that idleSessions == hcSessions == mockSessions.
|
||||
if !reflect.DeepEqual(idleSessions, hcSessions) {
|
||||
if !testEqual(idleSessions, hcSessions) {
|
||||
t.Errorf("%v: sessions in idle list (%v) != sessions in healthcheck queue (%v)", ti, idleSessions, hcSessions)
|
||||
}
|
||||
if !reflect.DeepEqual(hcSessions, mockSessions) {
|
||||
if !testEqual(hcSessions, mockSessions) {
|
||||
t.Errorf("%v: sessions in healthcheck queue (%v) != sessions in mockclient (%v)", ti, hcSessions, mockSessions)
|
||||
}
|
||||
sp.close()
|
||||
@@ -899,3 +841,17 @@ func TestMaintainer(t *testing.T) {
|
||||
}
|
||||
sp.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s1 *session) Equal(s2 *session) bool {
|
||||
return s1.client == s2.client &&
|
||||
s1.id == s2.id &&
|
||||
s1.pool == s2.pool &&
|
||||
s1.createTime == s2.createTime &&
|
||||
s1.valid == s2.valid &&
|
||||
s1.hcIndex == s2.hcIndex &&
|
||||
s1.idleList == s2.idleList &&
|
||||
s1.nextCheck.Equal(s2.nextCheck) &&
|
||||
s1.checkingHealth == s2.checkingHealth &&
|
||||
testEqual(s1.md, s2.md) &&
|
||||
bytes.Equal(s1.tx, s2.tx)
|
||||
}
|
||||
|
||||
+74
-18
@@ -114,7 +114,7 @@ func initIntegrationTest() {
|
||||
ctx := context.Background()
|
||||
ts := testutil.TokenSource(ctx, AdminScope, Scope)
|
||||
if ts == nil {
|
||||
log.Print("Integration test skipped: cannot get service account credential from environment variable %v", "GCLOUD_TESTS_GOLANG_KEY")
|
||||
log.Printf("Integration test skipped: cannot get service account credential from environment variable %v", "GCLOUD_TESTS_GOLANG_KEY")
|
||||
return
|
||||
}
|
||||
var err error
|
||||
@@ -164,7 +164,8 @@ func prepare(ctx context.Context, t *testing.T, statements []string) (client *Cl
|
||||
}
|
||||
return client, dbPath, func() {
|
||||
if err := admin.DropDatabase(ctx, &adminpb.DropDatabaseRequest{dbPath}); err != nil {
|
||||
t.Logf("failed to drop testing database: %v, might need a manual removal", dbPath)
|
||||
t.Logf("failed to drop database %s (error %v), might need a manual removal",
|
||||
dbPath, err)
|
||||
}
|
||||
client.Close()
|
||||
}
|
||||
@@ -173,7 +174,7 @@ func prepare(ctx context.Context, t *testing.T, statements []string) (client *Cl
|
||||
// Test SingleUse transaction.
|
||||
func TestSingleUse(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
// Set up testing environment.
|
||||
client, _, tearDown := prepare(ctx, t, singerDBStatements)
|
||||
@@ -278,7 +279,7 @@ func TestSingleUse(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%d: SingleUse.Query returns error %v, want nil", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: got unexpected result from SingleUse.Query: %v, want %v", i, got, test.want)
|
||||
}
|
||||
rts, err := su.Timestamp()
|
||||
@@ -294,7 +295,7 @@ func TestSingleUse(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%d: SingleUse.Read returns error %v, want nil", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: got unexpected result from SingleUse.Read: %v, want %v", i, got, test.want)
|
||||
}
|
||||
rts, err = su.Timestamp()
|
||||
@@ -325,7 +326,7 @@ func TestSingleUse(t *testing.T) {
|
||||
t.Errorf("%d: SingleUse.ReadRow(%v) doesn't return expected timestamp: %v", i, k, err)
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: got unexpected results from SingleUse.ReadRow: %v, want %v", i, got, test.want)
|
||||
}
|
||||
// SingleUse.ReadUsingIndex
|
||||
@@ -348,7 +349,7 @@ func TestSingleUse(t *testing.T) {
|
||||
}
|
||||
found := false
|
||||
for _, w := range test.want {
|
||||
if reflect.DeepEqual(g, w) {
|
||||
if testEqual(g, w) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@@ -365,6 +366,19 @@ func TestSingleUse(t *testing.T) {
|
||||
t.Errorf("%d: SingleUse.ReadUsingIndex doesn't return expected timestamp: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reading with limit.
|
||||
su := client.Single()
|
||||
const limit = 1
|
||||
gotRows, err := readAll(su.ReadWithOptions(ctx, "Singers", KeySets(Key{1}, Key{3}, Key{4}),
|
||||
[]string{"SingerId", "FirstName", "LastName"}, &ReadOptions{Limit: limit}))
|
||||
if err != nil {
|
||||
t.Errorf("SingleUse.ReadWithOptions returns error %v, want nil", err)
|
||||
}
|
||||
if got, want := len(gotRows), limit; got != want {
|
||||
t.Errorf("got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Test ReadOnlyTransaction. The testsuite is mostly like SingleUse, except it
|
||||
@@ -455,7 +469,7 @@ func TestReadOnlyTransaction(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%d: ReadOnlyTransaction.Query returns error %v, want nil", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: got unexpected result from ReadOnlyTransaction.Query: %v, want %v", i, got, test.want)
|
||||
}
|
||||
rts, err := ro.Timestamp()
|
||||
@@ -471,7 +485,7 @@ func TestReadOnlyTransaction(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("%d: ReadOnlyTransaction.Read returns error %v, want nil", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: got unexpected result from ReadOnlyTransaction.Read: %v, want %v", i, got, test.want)
|
||||
}
|
||||
rts, err = ro.Timestamp()
|
||||
@@ -507,7 +521,7 @@ func TestReadOnlyTransaction(t *testing.T) {
|
||||
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) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: got unexpected results from ReadOnlyTransaction.ReadRow: %v, want %v", i, got, test.want)
|
||||
}
|
||||
// SingleUse.ReadUsingIndex
|
||||
@@ -529,7 +543,7 @@ func TestReadOnlyTransaction(t *testing.T) {
|
||||
}
|
||||
found := false
|
||||
for _, w := range test.want {
|
||||
if reflect.DeepEqual(g, w) {
|
||||
if testEqual(g, w) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@@ -587,7 +601,7 @@ func TestUpdateDuringRead(t *testing.T) {
|
||||
func TestReadWriteTransaction(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Give a longer deadline because of transaction backoffs.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancel()
|
||||
client, _, tearDown := prepare(ctx, t, singerDBStatements)
|
||||
defer tearDown()
|
||||
@@ -832,7 +846,7 @@ func compareRows(iter *RowIterator, wantNums []int) (string, bool) {
|
||||
for _, r := range rows {
|
||||
got[r.Key] = r.StringValue
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
return fmt.Sprintf("got %v, want %v", got, want), false
|
||||
}
|
||||
return "", true
|
||||
@@ -842,7 +856,7 @@ func TestEarlyTimestamp(t *testing.T) {
|
||||
t.Parallel()
|
||||
// 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(), 45*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
// Set up testing environment.
|
||||
client, _, tearDown := prepare(ctx, t, readDBStatements)
|
||||
@@ -1092,7 +1106,7 @@ func TestBasicTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check non-NaN cases.
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("%d: col:%v val:%#v, got %#v, want %#v", i, test.col, test.val, got, want)
|
||||
continue
|
||||
}
|
||||
@@ -1164,7 +1178,7 @@ func TestStructTypes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(want, s) {
|
||||
if !testEqual(want, s) {
|
||||
return fmt.Errorf("unexpected decoding result: %v, want %v", s, want)
|
||||
}
|
||||
return nil
|
||||
@@ -1235,12 +1249,54 @@ func TestQueryExpressions(t *testing.T) {
|
||||
if isNaN(got) && isNaN(test.want) {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%q\n got %#v\nwant %#v", test.expr, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryStats(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client, _, tearDown := prepare(ctx, t, singerDBStatements)
|
||||
defer tearDown()
|
||||
|
||||
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)
|
||||
}
|
||||
const sql = "SELECT Balance FROM Accounts"
|
||||
|
||||
qp, err := client.Single().AnalyzeQuery(ctx, Statement{sql, nil})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(qp.PlanNodes) == 0 {
|
||||
t.Error("got zero plan nodes, expected at least one")
|
||||
}
|
||||
|
||||
iter := client.Single().QueryWithStats(ctx, Statement{sql, nil})
|
||||
defer iter.Stop()
|
||||
for {
|
||||
_, err := iter.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if iter.QueryPlan == nil {
|
||||
t.Error("got nil QueryPlan, expected one")
|
||||
}
|
||||
if iter.QueryStats == nil {
|
||||
t.Error("got nil QueryStats, expected some")
|
||||
}
|
||||
}
|
||||
|
||||
func isNaN(x interface{}) bool {
|
||||
f, ok := x.(float64)
|
||||
if !ok {
|
||||
@@ -1260,7 +1316,7 @@ func TestInvalidDatabase(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ts := testutil.TokenSource(ctx, Scope)
|
||||
if ts == nil {
|
||||
t.Skip("Integration test skipped: cannot get service account credential from environment variable %v", "GCLOUD_TESTS_GOLANG_KEY")
|
||||
t.Skip("Integration test skipped: cannot get service account credential from environment variable GCLOUD_TESTS_GOLANG_KEY")
|
||||
}
|
||||
db := fmt.Sprintf("projects/%v/instances/%v/databases/invalid", testProjectID, testInstanceID)
|
||||
c, err := NewClient(ctx, db, option.WithTokenSource(ts))
|
||||
|
||||
+1
-2
@@ -18,7 +18,6 @@ package spanner
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -158,7 +157,7 @@ func TestBindParams(t *testing.T) {
|
||||
} {
|
||||
st.Params["var"] = test.val
|
||||
var got sppb.ExecuteSqlRequest
|
||||
if err := st.bindParams(&got); !reflect.DeepEqual(err, test.wantErr) {
|
||||
if err := st.bindParams(&got); !testEqual(err, test.wantErr) {
|
||||
t.Errorf("value %#v:\ngot: %v\nwant: %v", test.val, err, test.wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-9
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package spanner
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -31,7 +30,7 @@ import (
|
||||
func TestStrong(t *testing.T) {
|
||||
got := StrongRead()
|
||||
want := TimestampBound{mode: strong}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("Strong() = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +39,7 @@ func TestStrong(t *testing.T) {
|
||||
func TestExactStaleness(t *testing.T) {
|
||||
got := ExactStaleness(10 * time.Second)
|
||||
want := TimestampBound{mode: exactStaleness, d: 10 * time.Second}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("ExactStaleness(10*time.Second) = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -49,7 +48,7 @@ func TestExactStaleness(t *testing.T) {
|
||||
func TestMaxStaleness(t *testing.T) {
|
||||
got := MaxStaleness(10 * time.Second)
|
||||
want := TimestampBound{mode: maxStaleness, d: 10 * time.Second}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("MaxStaleness(10*time.Second) = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -59,7 +58,7 @@ func TestMinReadTimestamp(t *testing.T) {
|
||||
ts := time.Now()
|
||||
got := MinReadTimestamp(ts)
|
||||
want := TimestampBound{mode: minReadTimestamp, t: ts}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("MinReadTimestamp(%v) = %v; want %v", ts, got, want)
|
||||
}
|
||||
}
|
||||
@@ -69,7 +68,7 @@ func TestReadTimestamp(t *testing.T) {
|
||||
ts := time.Now()
|
||||
got := ReadTimestamp(ts)
|
||||
want := TimestampBound{mode: readTimestamp, t: ts}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !testEqual(got, want) {
|
||||
t.Errorf("ReadTimestamp(%v) = %v; want %v", ts, got, want)
|
||||
}
|
||||
}
|
||||
@@ -125,7 +124,7 @@ func TestDurationProto(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := durationProto(test.d)
|
||||
if !reflect.DeepEqual(got, &test.want) {
|
||||
if !testEqual(got, &test.want) {
|
||||
t.Errorf("durationProto(%v) = %v; want %v", test.d, got, test.want)
|
||||
}
|
||||
}
|
||||
@@ -143,7 +142,7 @@ func TestTimeProto(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := timestampProto(test.t)
|
||||
if !reflect.DeepEqual(got, &test.want) {
|
||||
if !testEqual(got, &test.want) {
|
||||
t.Errorf("timestampProto(%v) = %v; want %v", test.t, got, test.want)
|
||||
}
|
||||
}
|
||||
@@ -201,7 +200,7 @@ func TestBuildTransactionOptionsReadOnly(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := buildTransactionOptionsReadOnly(test.tb, test.ts)
|
||||
if !reflect.DeepEqual(got, &test.want) {
|
||||
if !testEqual(got, &test.want) {
|
||||
t.Errorf("buildTransactionOptionsReadOnly(%v,%v) = %v; want %v", test.tb, test.ts, got, test.want)
|
||||
}
|
||||
}
|
||||
|
||||
+68
-10
@@ -56,17 +56,30 @@ func errSessionClosed(sh *sessionHandle) error {
|
||||
|
||||
// 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)
|
||||
return t.ReadWithOptions(ctx, table, keys, columns, nil)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// ReadUsingIndex calls ReadWithOptions with ReadOptions{Index: index}.
|
||||
func (t *txReadOnly) ReadUsingIndex(ctx context.Context, table, index string, keys KeySet, columns []string) (ri *RowIterator) {
|
||||
return t.ReadWithOptions(ctx, table, keys, columns, &ReadOptions{Index: index})
|
||||
}
|
||||
|
||||
// ReadOptions provides options for reading rows from a database.
|
||||
type ReadOptions struct {
|
||||
// The index to use for reading. If non-empty, you 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.
|
||||
Index string
|
||||
|
||||
// The maximum number of rows to read. A limit value less than 1 means no limit.
|
||||
Limit int
|
||||
}
|
||||
|
||||
// ReadWithOptions returns a RowIterator for reading multiple rows from the database.
|
||||
// Pass a ReadOptions to modify the read operation.
|
||||
func (t *txReadOnly) ReadWithOptions(ctx context.Context, table string, keys KeySet, columns []string, opts *ReadOptions) (ri *RowIterator) {
|
||||
ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.Read")
|
||||
defer func() { traceEndSpan(ctx, ri.err) }()
|
||||
var (
|
||||
sh *sessionHandle
|
||||
ts *sppb.TransactionSelector
|
||||
@@ -85,6 +98,14 @@ func (t *txReadOnly) ReadUsingIndex(ctx context.Context, table, index string, ke
|
||||
// Might happen if transaction is closed in the middle of a API call.
|
||||
return &RowIterator{err: errSessionClosed(sh)}
|
||||
}
|
||||
index := ""
|
||||
limit := 0
|
||||
if opts != nil {
|
||||
index = opts.Index
|
||||
if opts.Limit > 0 {
|
||||
limit = opts.Limit
|
||||
}
|
||||
}
|
||||
return stream(
|
||||
contextWithOutgoingMetadata(ctx, sh.getMetadata()),
|
||||
func(ctx context.Context, resumeToken []byte) (streamingReceiver, error) {
|
||||
@@ -97,6 +118,7 @@ func (t *txReadOnly) ReadUsingIndex(ctx context.Context, table, index string, ke
|
||||
Columns: columns,
|
||||
KeySet: kset,
|
||||
ResumeToken: resumeToken,
|
||||
Limit: int64(limit),
|
||||
})
|
||||
},
|
||||
t.setTimestamp,
|
||||
@@ -129,7 +151,42 @@ func (t *txReadOnly) ReadRow(ctx context.Context, table string, key Key, columns
|
||||
|
||||
// Query executes a query against the database. It returns a RowIterator
|
||||
// for retrieving the resulting rows.
|
||||
//
|
||||
// Query returns only row data, without a query plan or execution statistics.
|
||||
// Use QueryWithStats to get rows along with the plan and statistics.
|
||||
// Use AnalyzeQuery to get just the plan.
|
||||
func (t *txReadOnly) Query(ctx context.Context, statement Statement) *RowIterator {
|
||||
return t.query(ctx, statement, sppb.ExecuteSqlRequest_NORMAL)
|
||||
}
|
||||
|
||||
// Query executes a query against the database. It returns a RowIterator
|
||||
// for retrieving the resulting rows. The RowIterator will also be populated
|
||||
// with a query plan and execution statistics.
|
||||
func (t *txReadOnly) QueryWithStats(ctx context.Context, statement Statement) *RowIterator {
|
||||
return t.query(ctx, statement, sppb.ExecuteSqlRequest_PROFILE)
|
||||
}
|
||||
|
||||
// AnalyzeQuery returns the query plan for statement.
|
||||
func (t *txReadOnly) AnalyzeQuery(ctx context.Context, statement Statement) (*sppb.QueryPlan, error) {
|
||||
iter := t.query(ctx, statement, sppb.ExecuteSqlRequest_PLAN)
|
||||
for {
|
||||
_, err := iter.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if iter.QueryPlan == nil {
|
||||
return nil, spannerErrorf(codes.Internal, "query plan unavailable")
|
||||
}
|
||||
return iter.QueryPlan, nil
|
||||
}
|
||||
|
||||
func (t *txReadOnly) query(ctx context.Context, statement Statement, mode sppb.ExecuteSqlRequest_QueryMode) (ri *RowIterator) {
|
||||
ctx = traceStartSpan(ctx, "cloud.google.com/go/spanner.Query")
|
||||
defer func() { traceEndSpan(ctx, ri.err) }()
|
||||
var (
|
||||
sh *sessionHandle
|
||||
ts *sppb.TransactionSelector
|
||||
@@ -148,6 +205,7 @@ func (t *txReadOnly) Query(ctx context.Context, statement Statement) *RowIterato
|
||||
Session: sid,
|
||||
Transaction: ts,
|
||||
Sql: statement.SQL,
|
||||
QueryMode: mode,
|
||||
}
|
||||
if err := statement.bindParams(req); err != nil {
|
||||
return &RowIterator{err: err}
|
||||
@@ -742,7 +800,7 @@ func (t *ReadWriteTransaction) runInTransaction(ctx context.Context, f func(cont
|
||||
return ts, err
|
||||
}
|
||||
// err == nil, return commit timestamp.
|
||||
return ts, err
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// writeOnlyTransaction provides the most efficient way of doing write-only transactions. It essentially does blind writes to Cloud Spanner.
|
||||
|
||||
+52
-39
@@ -18,7 +18,6 @@ package spanner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -60,12 +59,12 @@ func TestReadOnlyAcquire(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, mc, client := mockClient(t)
|
||||
defer client.Close()
|
||||
acts := []testutil.Action{
|
||||
testutil.NewAction("Begin", errUsr),
|
||||
testutil.NewAction("Begin", nil),
|
||||
testutil.NewAction("Begin", nil),
|
||||
}
|
||||
mc.SetActions(acts...)
|
||||
mc.SetActions(
|
||||
testutil.Action{"BeginTransaction", errUsr},
|
||||
testutil.Action{"BeginTransaction", nil},
|
||||
testutil.Action{"BeginTransaction", nil},
|
||||
)
|
||||
|
||||
// Singleuse should only be used once.
|
||||
txn := client.Single()
|
||||
defer txn.Close()
|
||||
@@ -74,13 +73,13 @@ func TestReadOnlyAcquire(t *testing.T) {
|
||||
t.Errorf("Acquire for single use, got %v, want nil.", e)
|
||||
}
|
||||
_, _, e = txn.acquire(context.Background())
|
||||
if wantErr := errTxClosed(); !reflect.DeepEqual(e, wantErr) {
|
||||
if wantErr := errTxClosed(); !testEqual(e, wantErr) {
|
||||
t.Errorf("Second acquire for single use, got %v, want %v.", e, wantErr)
|
||||
}
|
||||
// Multiuse can recover from acquire failure.
|
||||
txn = client.ReadOnlyTransaction()
|
||||
_, _, e = txn.acquire(context.Background())
|
||||
if wantErr := toSpannerError(errUsr); !reflect.DeepEqual(e, wantErr) {
|
||||
if wantErr := toSpannerError(errUsr); !testEqual(e, wantErr) {
|
||||
t.Errorf("Acquire for multi use, got %v, want %v.", e, wantErr)
|
||||
}
|
||||
_, _, e = txn.acquire(context.Background())
|
||||
@@ -90,7 +89,7 @@ func TestReadOnlyAcquire(t *testing.T) {
|
||||
txn.Close()
|
||||
// Multiuse can not be used after close.
|
||||
_, _, e = txn.acquire(context.Background())
|
||||
if wantErr := errTxClosed(); !reflect.DeepEqual(e, wantErr) {
|
||||
if wantErr := errTxClosed(); !testEqual(e, wantErr) {
|
||||
t.Errorf("Second acquire for multi use, got %v, want %v.", e, wantErr)
|
||||
}
|
||||
// Multiuse can be acquired concurrently.
|
||||
@@ -118,10 +117,10 @@ func TestReadOnlyAcquire(t *testing.T) {
|
||||
<-time.After(100 * time.Millisecond)
|
||||
mc.Unfreeze()
|
||||
wg.Wait()
|
||||
if !reflect.DeepEqual(sh1, sh2) {
|
||||
if !testEqual(sh1.session, sh2.session) {
|
||||
t.Errorf("Expect acquire to get same session handle, got %v and %v.", sh1, sh2)
|
||||
}
|
||||
if !reflect.DeepEqual(ts1, ts2) {
|
||||
if !testEqual(ts1, ts2) {
|
||||
t.Errorf("Expect acquire to get same transaction selector, got %v and %v.", ts1, ts2)
|
||||
}
|
||||
}
|
||||
@@ -132,11 +131,11 @@ func TestRetryOnAbort(t *testing.T) {
|
||||
_, mc, client := mockClient(t)
|
||||
defer client.Close()
|
||||
// commit in writeOnlyTransaction
|
||||
acts := []testutil.Action{
|
||||
testutil.NewAction("Commit", errAbrt), // abort on first commit
|
||||
testutil.NewAction("Commit", nil),
|
||||
}
|
||||
mc.SetActions(acts...)
|
||||
mc.SetActions(
|
||||
testutil.Action{"Commit", errAbrt}, // abort on first commit
|
||||
testutil.Action{"Commit", nil},
|
||||
)
|
||||
|
||||
ms := []*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)}),
|
||||
@@ -145,14 +144,14 @@ func TestRetryOnAbort(t *testing.T) {
|
||||
t.Errorf("applyAtLeastOnce retry on abort, got %v, want nil.", e)
|
||||
}
|
||||
// begin and commit in ReadWriteTransaction
|
||||
acts = []testutil.Action{
|
||||
testutil.NewAction("Begin", nil), // let takeWriteSession succeed and get a session handle
|
||||
testutil.NewAction("Commit", errAbrt), // let first commit fail and retry will begin new transaction
|
||||
testutil.NewAction("Begin", errAbrt), // this time we can fail the begin attempt
|
||||
testutil.NewAction("Begin", nil),
|
||||
testutil.NewAction("Commit", nil),
|
||||
}
|
||||
mc.SetActions(acts...)
|
||||
mc.SetActions(
|
||||
testutil.Action{"BeginTransaction", nil}, // let takeWriteSession succeed and get a session handle
|
||||
testutil.Action{"Commit", errAbrt}, // let first commit fail and retry will begin new transaction
|
||||
testutil.Action{"BeginTransaction", errAbrt}, // this time we can fail the begin attempt
|
||||
testutil.Action{"BeginTransaction", nil},
|
||||
testutil.Action{"Commit", nil},
|
||||
)
|
||||
|
||||
if _, e := client.Apply(context.Background(), ms); e != nil {
|
||||
t.Errorf("ReadWriteTransaction retry on abort, got %v, want nil.", e)
|
||||
}
|
||||
@@ -176,22 +175,21 @@ func TestBadSession(t *testing.T) {
|
||||
|
||||
wantErr := spannerErrorf(codes.NotFound, "Session not found: %v", sid)
|
||||
// ReadOnlyTransaction
|
||||
acts := []testutil.Action{
|
||||
testutil.NewAction("Begin", wantErr),
|
||||
testutil.NewAction("Begin", wantErr),
|
||||
testutil.NewAction("Begin", wantErr),
|
||||
}
|
||||
mc.SetActions(acts...)
|
||||
mc.SetActions(
|
||||
testutil.Action{"BeginTransaction", wantErr},
|
||||
testutil.Action{"BeginTransaction", wantErr},
|
||||
testutil.Action{"BeginTransaction", wantErr},
|
||||
)
|
||||
txn := client.ReadOnlyTransaction()
|
||||
defer txn.Close()
|
||||
if _, _, got := txn.acquire(ctx); !reflect.DeepEqual(wantErr, got) {
|
||||
if _, _, got := txn.acquire(ctx); !testEqual(wantErr, got) {
|
||||
t.Errorf("Expect acquire to fail, got %v, want %v.", got, wantErr)
|
||||
}
|
||||
// The failure should recycle the session, we expect it to be used in following requests.
|
||||
if got := txn.Query(ctx, NewStatement("SELECT 1")); !reflect.DeepEqual(wantErr, got.err) {
|
||||
if got := txn.Query(ctx, NewStatement("SELECT 1")); !testEqual(wantErr, got.err) {
|
||||
t.Errorf("Expect Query to fail, got %v, want %v.", got.err, wantErr)
|
||||
}
|
||||
if got := txn.Read(ctx, "Users", KeySets(Key{"alice"}, Key{"bob"}), []string{"name", "email"}); !reflect.DeepEqual(wantErr, got.err) {
|
||||
if got := txn.Read(ctx, "Users", KeySets(Key{"alice"}, Key{"bob"}), []string{"name", "email"}); !testEqual(wantErr, got.err) {
|
||||
t.Errorf("Expect Read to fail, got %v, want %v.", got.err, wantErr)
|
||||
}
|
||||
// writeOnlyTransaction
|
||||
@@ -199,11 +197,26 @@ func TestBadSession(t *testing.T) {
|
||||
Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(1), "Foo", int64(50)}),
|
||||
Insert("Accounts", []string{"AccountId", "Nickname", "Balance"}, []interface{}{int64(2), "Bar", int64(1)}),
|
||||
}
|
||||
acts = []testutil.Action{
|
||||
testutil.NewAction("Commit", wantErr),
|
||||
}
|
||||
mc.SetActions(acts...)
|
||||
if _, got := client.Apply(context.Background(), ms, ApplyAtLeastOnce()); !reflect.DeepEqual(wantErr, got) {
|
||||
mc.SetActions(testutil.Action{"Commit", wantErr})
|
||||
if _, got := client.Apply(context.Background(), ms, ApplyAtLeastOnce()); !testEqual(wantErr, got) {
|
||||
t.Errorf("Expect applyAtLeastOnce to fail, got %v, want %v.", got, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionErrorReturned(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, mc, client := mockClient(t)
|
||||
defer client.Close()
|
||||
mc.SetActions(
|
||||
testutil.Action{"BeginTransaction", nil},
|
||||
testutil.Action{"Rollback", nil},
|
||||
)
|
||||
|
||||
want := errors.New("an error")
|
||||
_, got := client.ReadWriteTransaction(context.Background(),
|
||||
func(context.Context, *ReadWriteTransaction) error { return want })
|
||||
if got != want {
|
||||
t.Errorf("got <%v>, want <%v>", got, want)
|
||||
}
|
||||
mc.CheckActionsConsumed()
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 (
|
||||
"cloud.google.com/go/internal/testutil"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func testEqual(a, b interface{}) bool {
|
||||
return testutil.Equal(a, b,
|
||||
cmp.AllowUnexported(TimestampBound{}, Error{}, Mutation{}, Row{}))
|
||||
}
|
||||
+4
-4
@@ -137,10 +137,10 @@ func TestEncodeValue(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("#%d: got error during encoding: %v, want nil", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("#%d: got encode result: %v, want %v", i, got, test.want)
|
||||
}
|
||||
if !reflect.DeepEqual(gotType, test.wantType) {
|
||||
if !testEqual(gotType, test.wantType) {
|
||||
t.Errorf("#%d: got encode type: %v, want %v", i, gotType, test.wantType)
|
||||
}
|
||||
}
|
||||
@@ -415,7 +415,7 @@ func TestDecodeValue(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
got := reflect.Indirect(gotp).Interface()
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
if !testEqual(got, test.want) {
|
||||
t.Errorf("%d: unexpected decoding result - got %v, want %v", i, got, test.want)
|
||||
continue
|
||||
}
|
||||
@@ -513,7 +513,7 @@ func TestGenericColumnValue(t *testing.T) {
|
||||
t.Errorf("NewGenericColumnValue failed: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(*v, test.in) {
|
||||
if !testEqual(*v, test.in) {
|
||||
t.Errorf("unexpected encode result - got %v, want %v", v, test.in)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user