diff --git a/spanner/errors.go b/spanner/errors.go index f4451812765a..ddf506bd555d 100644 --- a/spanner/errors.go +++ b/spanner/errors.go @@ -18,6 +18,7 @@ package spanner import ( "context" + "errors" "fmt" "github.com/googleapis/gax-go/v2/apierror" @@ -26,6 +27,11 @@ import ( "google.golang.org/grpc/status" ) +var ( + // ErrRowNotFound row not found error + ErrRowNotFound = errors.New("row not found") +) + // Error is the structured error returned by Cloud Spanner client. // // Deprecated: Unwrap any error that is returned by the Spanner client as an APIError diff --git a/spanner/integration_test.go b/spanner/integration_test.go index 8ea92ff7537c..2e1d215a2c4d 100644 --- a/spanner/integration_test.go +++ b/spanner/integration_test.go @@ -1731,6 +1731,9 @@ func TestIntegration_Reads(t *testing.T) { if ErrCode(err) != codes.NotFound { t.Fatalf("got %v, want NotFound", err) } + if !errors.Is(err, ErrRowNotFound) { + t.Fatalf("got %v, want spanner.ErrRowNotFound", err) + } verifyDirectPathRemoteAddress(t) // Index point read. @@ -1751,6 +1754,9 @@ func TestIntegration_Reads(t *testing.T) { if ErrCode(err) != codes.NotFound { t.Fatalf("got %v, want NotFound", err) } + if !errors.Is(err, ErrRowNotFound) { + t.Fatalf("got %v, want spanner.ErrRowNotFound", err) + } verifyDirectPathRemoteAddress(t) rangeReads(ctx, t, client) indexRangeReads(ctx, t, client) diff --git a/spanner/transaction.go b/spanner/transaction.go index 5fba4e856966..9a3306616c0e 100644 --- a/spanner/transaction.go +++ b/spanner/transaction.go @@ -326,12 +326,16 @@ func (t *txReadOnly) ReadWithOptions(ctx context.Context, table string, keys Key // errRowNotFound returns error for not being able to read the row identified by // key. func errRowNotFound(table string, key Key) error { - return spannerErrorf(codes.NotFound, "row not found(Table: %v, PrimaryKey: %v)", table, key) + err := spannerErrorf(codes.NotFound, "row not found(Table: %v, PrimaryKey: %v)", table, key) + err.(*Error).err = ErrRowNotFound + return err } // errRowNotFoundByIndex returns error for not being able to read the row by index. func errRowNotFoundByIndex(table string, key Key, index string) error { - return spannerErrorf(codes.NotFound, "row not found(Table: %v, IndexKey: %v, Index: %v)", table, key, index) + err := spannerErrorf(codes.NotFound, "row not found(Table: %v, IndexKey: %v, Index: %v)", table, key, index) + err.(*Error).err = ErrRowNotFound + return err } // errMultipleRowsFound returns error for receiving more than one row when reading a single row using an index. @@ -346,8 +350,14 @@ func errInlineBeginTransactionFailed() error { // ReadRow reads a single row from the database. // -// If no row is present with the given key, then ReadRow returns an error where +// If no row is present with the given key, then ReadRow returns an error(spanner.ErrRowNotFound) where // spanner.ErrCode(err) is codes.NotFound. +// +// To check if the error is spanner.ErrRowNotFound: +// +// if errors.Is(err, spanner.ErrRowNotFound) { +// ... +// } func (t *txReadOnly) ReadRow(ctx context.Context, table string, key Key, columns []string) (*Row, error) { return t.ReadRowWithOptions(ctx, table, key, columns, nil) } @@ -356,6 +366,12 @@ func (t *txReadOnly) ReadRow(ctx context.Context, table string, key Key, columns // // If no row is present with the given key, then ReadRowWithOptions returns an error where // spanner.ErrCode(err) is codes.NotFound. +// +// To check if the error is spanner.ErrRowNotFound: +// +// if errors.Is(err, spanner.ErrRowNotFound) { +// ... +// } func (t *txReadOnly) ReadRowWithOptions(ctx context.Context, table string, key Key, columns []string, opts *ReadOptions) (*Row, error) { iter := t.ReadWithOptions(ctx, table, key, columns, opts) defer iter.Stop() @@ -373,7 +389,13 @@ func (t *txReadOnly) ReadRowWithOptions(ctx context.Context, table string, key K // ReadRowUsingIndex reads a single row from the database using an index. // // If no row is present with the given index, then ReadRowUsingIndex returns an -// error where spanner.ErrCode(err) is codes.NotFound. +// error(spanner.ErrRowNotFound) where spanner.ErrCode(err) is codes.NotFound. +// +// To check if the error is spanner.ErrRowNotFound: +// +// if errors.Is(err, spanner.ErrRowNotFound) { +// ... +// } // // If more than one row received with the given index, then ReadRowUsingIndex // returns an error where spanner.ErrCode(err) is codes.FailedPrecondition.