Skip to content

Commit

Permalink
feat: export ToSpannerError
Browse files Browse the repository at this point in the history
The ToSpannerError method should be exported to allow users to create
Spanner errors to use with unit tests. Currently, that was only possible
by creating a struct directly and filling the deprecated Code field. That
is however no longer recommended, as the code field might be removed in a
future release.

Fixes #3122
  • Loading branch information
olavloite committed Nov 3, 2020
1 parent 8f5495d commit 1d92133
Show file tree
Hide file tree
Showing 12 changed files with 31 additions and 24 deletions.
4 changes: 2 additions & 2 deletions spanner/client.go
Expand Up @@ -117,7 +117,7 @@ type ClientConfig struct {

// errDial returns error for dialing to Cloud Spanner.
func errDial(ci int, err error) error {
e := toSpannerError(err).(*Error)
e := ToSpannerError(err).(*Error)
e.decorate(fmt.Sprintf("dialing fails for channel[%v]", ci))
return e
}
Expand Down Expand Up @@ -341,7 +341,7 @@ func (c *Client) BatchReadOnlyTransaction(ctx context.Context, tb TimestampBound
},
})
if err != nil {
return nil, toSpannerError(err)
return nil, ToSpannerError(err)
}
tx = res.Id
if res.ReadTimestamp != nil {
Expand Down
2 changes: 1 addition & 1 deletion spanner/client_test.go
Expand Up @@ -537,7 +537,7 @@ func TestClient_ReadOnlyTransaction_SessionNotFoundOnExecuteStreamingSql(t *test
err := testReadOnlyTransaction(t, map[string]SimulatedExecutionTime{
MethodExecuteStreamingSql: {Errors: []error{newSessionNotFoundError("projects/p/instances/i/databases/d/sessions/s")}},
})
want := toSpannerError(newSessionNotFoundError("projects/p/instances/i/databases/d/sessions/s"))
want := ToSpannerError(newSessionNotFoundError("projects/p/instances/i/databases/d/sessions/s"))
if err == nil {
t.Fatalf("missing expected error\nGot: nil\nWant: %v", want)
}
Expand Down
11 changes: 9 additions & 2 deletions spanner/errors.go
Expand Up @@ -115,8 +115,15 @@ func spannerErrorf(code codes.Code, format string, args ...interface{}) error {
}
}

// toSpannerError converts general Go error to *spanner.Error.
func toSpannerError(err error) error {
// ToSpannerError converts a general Go error to *spanner.Error. If the given
// error is already a *spanner.Error, the original error will be returned.
//
// Spanner Errors are normally created by the Spanner client library from the
// returned status of a RPC. This method can also be used to create Spanner
// errors for use in tests. The recommended way to create test errors is
// calling this method with a status error, e.g.
// ToSpannerError(status.Newf(codes.NotFound, "Table not found").Err())
func ToSpannerError(err error) error {
return toSpannerErrorWithCommitInfo(err, false)
}

Expand Down
2 changes: 1 addition & 1 deletion spanner/errors_test.go
Expand Up @@ -63,7 +63,7 @@ func TestToSpannerError(t *testing.T) {
wrapped: errors.New("wha?"),
msg: "error with wrapped non-gRPC and non-Spanner error"}},
} {
err := toSpannerError(test.err)
err := ToSpannerError(test.err)
errDuringCommit := toSpannerErrorWithCommitInfo(test.err, true)
if got, want := ErrCode(err), test.wantCode; got != want {
t.Errorf("%v: got %s, want %s", test.err, got, want)
Expand Down
2 changes: 1 addition & 1 deletion spanner/mutation.go
Expand Up @@ -179,7 +179,7 @@ func structToMutationParams(in interface{}) ([]string, []interface{}, error) {
}
fields, err := fieldCache.Fields(t)
if err != nil {
return nil, nil, toSpannerError(err)
return nil, nil, ToSpannerError(err)
}
var cols []string
var vals []interface{}
Expand Down
6 changes: 3 additions & 3 deletions spanner/pdml.go
Expand Up @@ -51,7 +51,7 @@ func (c *Client) partitionedUpdate(ctx context.Context, statement Statement, opt

sh, err := c.idleSessions.take(ctx)
if err != nil {
return 0, toSpannerError(err)
return 0, ToSpannerError(err)
}
if sh != nil {
defer sh.recycle()
Expand All @@ -61,7 +61,7 @@ func (c *Client) partitionedUpdate(ctx context.Context, statement Statement, opt
// The transaction reference will be added by the executePdml method.
params, paramTypes, err := statement.convertParams()
if err != nil {
return 0, toSpannerError(err)
return 0, ToSpannerError(err)
}
req := &sppb.ExecuteSqlRequest{
Session: sh.getID(),
Expand Down Expand Up @@ -107,7 +107,7 @@ func executePdml(ctx context.Context, sh *sessionHandle, req *sppb.ExecuteSqlReq
},
})
if err != nil {
return 0, toSpannerError(err)
return 0, ToSpannerError(err)
}
// Add a reference to the PDML transaction on the ExecuteSql request.
req.Transaction = &sppb.TransactionSelector{
Expand Down
2 changes: 1 addition & 1 deletion spanner/read.go
Expand Up @@ -157,7 +157,7 @@ func (r *RowIterator) Next() (*Row, error) {
return row, nil
}
if err := r.streamd.lastErr(); err != nil {
r.err = toSpannerError(err)
r.err = ToSpannerError(err)
} else if !r.rowd.done() {
r.err = errEarlyReadEnd()
} else {
Expand Down
6 changes: 3 additions & 3 deletions spanner/session.go
Expand Up @@ -1033,13 +1033,13 @@ func (p *sessionPool) takeWriteSession(ctx context.Context) (*sessionHandle, err
s.destroy(false)
trace.TracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
"Session not found for write")
return nil, toSpannerError(err)
return nil, ToSpannerError(err)
}

s.recycle()
trace.TracePrintf(ctx, map[string]interface{}{"sessionID": s.getID()},
"Error preparing session for write")
return nil, toSpannerError(err)
return nil, ToSpannerError(err)
}
}
p.incNumInUse(ctx)
Expand Down Expand Up @@ -1506,7 +1506,7 @@ func (hc *healthChecker) worker(i int) {
// cycle.
// Don't log about permission errors, which may be expected
// (e.g. using read-only auth).
serr := toSpannerError(err).(*Error)
serr := ToSpannerError(err).(*Error)
if serr.Code != codes.PermissionDenied {
logf(hc.pool.sc.logger, "Failed to prepare session, error: %v", serr)
}
Expand Down
6 changes: 3 additions & 3 deletions spanner/sessionclient.go
Expand Up @@ -135,7 +135,7 @@ func (sc *sessionClient) createSession(ctx context.Context) (*session, error) {
Session: &sppb.Session{Labels: sc.sessionLabels},
})
if err != nil {
return nil, toSpannerError(err)
return nil, ToSpannerError(err)
}
return &session{valid: true, client: client, id: sid.Name, createTime: time.Now(), md: sc.md, logger: sc.logger}, nil
}
Expand Down Expand Up @@ -227,7 +227,7 @@ func (sc *sessionClient) executeBatchCreateSessions(client *vkit.Client, createC
}
if ctx.Err() != nil {
trace.TracePrintf(ctx, nil, "Context error while creating a batch of %d sessions: %v", createCount, ctx.Err())
consumer.sessionCreationFailed(toSpannerError(ctx.Err()), remainingCreateCount)
consumer.sessionCreationFailed(ToSpannerError(ctx.Err()), remainingCreateCount)
break
}
response, err := client.BatchCreateSessions(ctx, &sppb.BatchCreateSessionsRequest{
Expand All @@ -237,7 +237,7 @@ func (sc *sessionClient) executeBatchCreateSessions(client *vkit.Client, createC
})
if err != nil {
trace.TracePrintf(ctx, nil, "Error creating a batch of %d sessions: %v", remainingCreateCount, err)
consumer.sessionCreationFailed(toSpannerError(err), remainingCreateCount)
consumer.sessionCreationFailed(ToSpannerError(err), remainingCreateCount)
break
}
actuallyCreated := int32(len(response.Session))
Expand Down
8 changes: 4 additions & 4 deletions spanner/transaction.go
Expand Up @@ -489,7 +489,7 @@ func (t *ReadOnlyTransaction) begin(ctx context.Context) error {
rts = time.Unix(res.ReadTimestamp.Seconds, int64(res.ReadTimestamp.Nanos))
}
} else {
err = toSpannerError(err)
err = ToSpannerError(err)
}
break
}
Expand Down Expand Up @@ -827,7 +827,7 @@ func (t *ReadWriteTransaction) update(ctx context.Context, stmt Statement, opts
}
resultSet, err := sh.getClient().ExecuteSql(contextWithOutgoingMetadata(ctx, sh.getMetadata()), req)
if err != nil {
return 0, toSpannerError(err)
return 0, ToSpannerError(err)
}
if resultSet.Stats == nil {
return 0, spannerErrorf(codes.InvalidArgument, "query passed to Update: %q", stmt.SQL)
Expand Down Expand Up @@ -876,7 +876,7 @@ func (t *ReadWriteTransaction) BatchUpdate(ctx context.Context, stmts []Statemen
Seqno: atomic.AddInt64(&t.sequenceNumber, 1),
})
if err != nil {
return nil, toSpannerError(err)
return nil, ToSpannerError(err)
}

var counts []int64
Expand Down Expand Up @@ -1209,7 +1209,7 @@ func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Muta
break
}
}
return ts, toSpannerError(err)
return ts, ToSpannerError(err)
}

// isAbortedErr returns true if the error indicates that an gRPC call is
Expand Down
4 changes: 2 additions & 2 deletions spanner/transaction_test.go
Expand Up @@ -78,7 +78,7 @@ func TestReadOnlyTransaction_RecoverFromFailure(t *testing.T) {
})

_, _, e := txn.acquire(ctx)
if wantErr := toSpannerError(errUsr); !testEqual(e, wantErr) {
if wantErr := ToSpannerError(errUsr); !testEqual(e, wantErr) {
t.Fatalf("Acquire for multi use, got %v, want %v.", e, wantErr)
}
_, _, e = txn.acquire(ctx)
Expand Down Expand Up @@ -238,7 +238,7 @@ func TestTransaction_SessionNotFound(t *testing.T) {
t.Fatalf("Expect Read to succeed, got %v, want %v.", got.err, wantErr)
}

wantErr = toSpannerError(newSessionNotFoundError("projects/p/instances/i/databases/d/sessions/s"))
wantErr = ToSpannerError(newSessionNotFoundError("projects/p/instances/i/databases/d/sessions/s"))
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)}),
Expand Down
2 changes: 1 addition & 1 deletion spanner/value.go
Expand Up @@ -2389,7 +2389,7 @@ func decodeStruct(ty *sppb.StructType, pb *proto3.ListValue, ptr interface{}) er

fields, err := fieldCache.Fields(t)
if err != nil {
return toSpannerError(err)
return ToSpannerError(err)
}
seen := map[string]bool{}
for i, f := range ty.Fields {
Expand Down

0 comments on commit 1d92133

Please sign in to comment.