Skip to content

Commit dd94d62

Browse files
committed
Merge branch 'spanner-lib-dotnet-wrapper' into spanner-lib-grpc-server
2 parents 9229a2d + d8e8ac0 commit dd94d62

File tree

14 files changed

+140
-61
lines changed

14 files changed

+140
-61
lines changed

benchmarks/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
cloud.google.com/go v0.122.0
1111
cloud.google.com/go/spanner v1.85.1
1212
github.com/google/uuid v1.6.0
13-
github.com/googleapis/go-sql-spanner v1.17.0
13+
github.com/googleapis/go-sql-spanner v1.18.0
1414
google.golang.org/api v0.249.0
1515
google.golang.org/grpc v1.75.1
1616
google.golang.org/protobuf v1.36.9

checksum_row_iterator.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"cloud.google.com/go/spanner"
2525
sppb "cloud.google.com/go/spanner/apiv1/spannerpb"
26+
"github.com/googleapis/go-sql-spanner/parser"
2627
"google.golang.org/api/iterator"
2728
"google.golang.org/grpc/codes"
2829
"google.golang.org/grpc/status"
@@ -51,10 +52,11 @@ type checksumRowIterator struct {
5152
*spanner.RowIterator
5253
metadata *sppb.ResultSetMetadata
5354

54-
ctx context.Context
55-
tx *readWriteTransaction
56-
stmt spanner.Statement
57-
options spanner.QueryOptions
55+
ctx context.Context
56+
tx *readWriteTransaction
57+
stmt spanner.Statement
58+
stmtType parser.StatementType
59+
options spanner.QueryOptions
5860
// nc (nextCount) indicates the number of times that next has been called
5961
// on the iterator. Next() will be called the same number of times during
6062
// a retry.
@@ -253,10 +255,5 @@ func (it *checksumRowIterator) Metadata() (*sppb.ResultSetMetadata, error) {
253255
}
254256

255257
func (it *checksumRowIterator) ResultSetStats() *sppb.ResultSetStats {
256-
// TODO: The Spanner client library should offer an option to get the full
257-
// ResultSetStats, instead of only the RowCount and QueryPlan.
258-
return &sppb.ResultSetStats{
259-
RowCount: &sppb.ResultSetStats_RowCountExact{RowCountExact: it.RowIterator.RowCount},
260-
QueryPlan: it.RowIterator.QueryPlan,
261-
}
258+
return createResultSetStats(it.RowIterator, it.stmtType)
262259
}

conn.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ type conn struct {
262262
resetForRetry bool
263263
database string
264264

265-
execSingleQuery func(ctx context.Context, c *spanner.Client, statement spanner.Statement, bound spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator
266-
execSingleQueryTransactional func(ctx context.Context, c *spanner.Client, statement spanner.Statement, options *ExecOptions) (rowIterator, *spanner.CommitResponse, error)
265+
execSingleQuery func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, bound spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator
266+
execSingleQueryTransactional func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (rowIterator, *spanner.CommitResponse, error)
267267
execSingleDMLTransactional func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (*result, *spanner.CommitResponse, error)
268268
execSingleDMLPartitioned func(ctx context.Context, c *spanner.Client, statement spanner.Statement, options *ExecOptions) (int64, error)
269269

@@ -860,9 +860,9 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions *Exec
860860
if err != nil {
861861
return nil, err
862862
}
863-
statementType := c.parser.DetectStatementType(query)
863+
statementInfo := c.parser.DetectStatementType(query)
864864
// DDL statements are not supported in QueryContext so use the execContext method for the execution.
865-
if statementType.StatementType == parser.StatementTypeDdl {
865+
if statementInfo.StatementType == parser.StatementTypeDdl {
866866
res, err := c.execContext(ctx, query, execOptions, args)
867867
if err != nil {
868868
return nil, err
@@ -871,10 +871,10 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions *Exec
871871
}
872872
var iter rowIterator
873873
if c.tx == nil {
874-
if statementType.StatementType == parser.StatementTypeDml {
874+
if statementInfo.StatementType == parser.StatementTypeDml {
875875
// Use a read/write transaction to execute the statement.
876876
var commitResponse *spanner.CommitResponse
877-
iter, commitResponse, err = c.execSingleQueryTransactional(ctx, c.client, stmt, execOptions)
877+
iter, commitResponse, err = c.execSingleQueryTransactional(ctx, c.client, stmt, statementInfo, execOptions)
878878
if err != nil {
879879
return nil, err
880880
}
@@ -887,13 +887,13 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions *Exec
887887
// The statement was either detected as being a query, or potentially not recognized at all.
888888
// In that case, just default to using a single-use read-only transaction and let Spanner
889889
// return an error if the statement is not suited for that type of transaction.
890-
iter = &readOnlyRowIterator{c.execSingleQuery(ctx, c.client, stmt, c.ReadOnlyStaleness(), execOptions)}
890+
iter = &readOnlyRowIterator{c.execSingleQuery(ctx, c.client, stmt, statementInfo, c.ReadOnlyStaleness(), execOptions), statementInfo.StatementType}
891891
}
892892
} else {
893893
if execOptions.PartitionedQueryOptions.PartitionQuery {
894894
return c.tx.partitionQuery(ctx, stmt, execOptions)
895895
}
896-
iter, err = c.tx.Query(ctx, stmt, execOptions)
896+
iter, err = c.tx.Query(ctx, stmt, statementInfo.StatementType, execOptions)
897897
if err != nil {
898898
return nil, err
899899
}
@@ -1341,7 +1341,7 @@ func (c *conn) Rollback(ctx context.Context) error {
13411341
return c.tx.Rollback()
13421342
}
13431343

1344-
func queryInSingleUse(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
1344+
func queryInSingleUse(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
13451345
return c.Single().WithTimestampBound(tb).QueryWithOptions(ctx, statement, options.QueryOptions)
13461346
}
13471347

@@ -1363,7 +1363,7 @@ func (c *conn) executeAutoPartitionedQuery(ctx context.Context, query string, ex
13631363
return r, nil
13641364
}
13651365

1366-
func queryInNewRWTransaction(ctx context.Context, c *spanner.Client, statement spanner.Statement, options *ExecOptions) (rowIterator, *spanner.CommitResponse, error) {
1366+
func queryInNewRWTransaction(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (rowIterator, *spanner.CommitResponse, error) {
13671367
var result *wrappedRowIterator
13681368
options.QueryOptions.LastStatement = true
13691369
fn := func(ctx context.Context, tx *spanner.ReadWriteTransaction) error {
@@ -1372,6 +1372,7 @@ func queryInNewRWTransaction(ctx context.Context, c *spanner.Client, statement s
13721372
if err == iterator.Done {
13731373
result = &wrappedRowIterator{
13741374
RowIterator: it,
1375+
stmtType: statementInfo.StatementType,
13751376
noRows: true,
13761377
}
13771378
} else if err != nil {
@@ -1380,6 +1381,7 @@ func queryInNewRWTransaction(ctx context.Context, c *spanner.Client, statement s
13801381
} else {
13811382
result = &wrappedRowIterator{
13821383
RowIterator: it,
1384+
stmtType: statementInfo.StatementType,
13831385
firstRow: row,
13841386
}
13851387
}

driver_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ func TestConn_NonDdlStatementsInDdlBatch(t *testing.T) {
630630
logger: noopLogger,
631631
state: createInitialConnectionState(connectionstate.TypeNonTransactional, map[string]connectionstate.ConnectionPropertyValue{}),
632632
batch: &batch{tp: parser.BatchTypeDdl},
633-
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
633+
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
634634
return &spanner.RowIterator{}
635635
},
636636
execSingleDMLTransactional: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (*result, *spanner.CommitResponse, error) {
@@ -670,7 +670,7 @@ func TestConn_NonDmlStatementsInDmlBatch(t *testing.T) {
670670
logger: noopLogger,
671671
state: createInitialConnectionState(connectionstate.TypeNonTransactional, map[string]connectionstate.ConnectionPropertyValue{}),
672672
batch: &batch{tp: parser.BatchTypeDml},
673-
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
673+
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
674674
return &spanner.RowIterator{}
675675
},
676676
execSingleDMLTransactional: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (*result, *spanner.CommitResponse, error) {
@@ -761,7 +761,7 @@ func TestConn_GetCommitResponseAfterAutocommitDml(t *testing.T) {
761761
parser: p,
762762
logger: noopLogger,
763763
state: createInitialConnectionState(connectionstate.TypeNonTransactional, map[string]connectionstate.ConnectionPropertyValue{}),
764-
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
764+
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
765765
return &spanner.RowIterator{}
766766
},
767767
execSingleDMLTransactional: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (*result, *spanner.CommitResponse, error) {
@@ -800,7 +800,7 @@ func TestConn_GetCommitResponseAfterAutocommitQuery(t *testing.T) {
800800
parser: p,
801801
logger: noopLogger,
802802
state: createInitialConnectionState(connectionstate.TypeTransactional, map[string]connectionstate.ConnectionPropertyValue{}),
803-
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
803+
execSingleQuery: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, tb spanner.TimestampBound, options *ExecOptions) *spanner.RowIterator {
804804
return &spanner.RowIterator{}
805805
},
806806
execSingleDMLTransactional: func(ctx context.Context, c *spanner.Client, statement spanner.Statement, statementInfo *parser.StatementInfo, options *ExecOptions) (*result, *spanner.CommitResponse, error) {

driver_with_mockserver_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5384,6 +5384,73 @@ func TestReturnResultSetStats(t *testing.T) {
53845384
}
53855385
}
53865386

5387+
func TestReturnResultSetStatsForQuery(t *testing.T) {
5388+
t.Parallel()
5389+
5390+
db, server, teardown := setupTestDBConnection(t)
5391+
defer teardown()
5392+
query := "select id from singers where id=42598"
5393+
resultSet := testutil.CreateSingleColumnInt64ResultSet([]int64{42598}, "id")
5394+
_ = server.TestSpanner.PutStatementResult(query, &testutil.StatementResult{
5395+
Type: testutil.StatementResultResultSet,
5396+
ResultSet: resultSet,
5397+
})
5398+
5399+
rows, err := db.QueryContext(context.Background(), query, ExecOptions{ReturnResultSetStats: true})
5400+
if err != nil {
5401+
t.Fatal(err)
5402+
}
5403+
defer func() { _ = rows.Close() }()
5404+
5405+
// The first result set should contain the data.
5406+
for want := int64(42598); rows.Next(); want++ {
5407+
cols, err := rows.Columns()
5408+
if err != nil {
5409+
t.Fatal(err)
5410+
}
5411+
if !cmp.Equal(cols, []string{"id"}) {
5412+
t.Fatalf("cols mismatch\nGot: %v\nWant: %v", cols, []string{"id"})
5413+
}
5414+
var got int64
5415+
err = rows.Scan(&got)
5416+
if err != nil {
5417+
t.Fatal(err)
5418+
}
5419+
if got != want {
5420+
t.Fatalf("value mismatch\nGot: %v\nWant: %v", got, want)
5421+
}
5422+
}
5423+
if rows.Err() != nil {
5424+
t.Fatal(rows.Err())
5425+
}
5426+
5427+
// The next result set should contain the stats.
5428+
if !rows.NextResultSet() {
5429+
t.Fatal("missing stats result set")
5430+
}
5431+
5432+
// Get the stats.
5433+
if !rows.Next() {
5434+
t.Fatal("no stats rows")
5435+
}
5436+
var stats *sppb.ResultSetStats
5437+
if err := rows.Scan(&stats); err != nil {
5438+
t.Fatalf("failed to scan stats: %v", err)
5439+
}
5440+
// The stats should not contain any update count.
5441+
if stats.GetRowCount() != nil {
5442+
t.Fatalf("got update count for query")
5443+
}
5444+
if rows.Next() {
5445+
t.Fatal("more rows than expected")
5446+
}
5447+
5448+
// There should be no more result sets.
5449+
if rows.NextResultSet() {
5450+
t.Fatal("more result sets than expected")
5451+
}
5452+
}
5453+
53875454
func TestReturnResultSetMetadataAndStats(t *testing.T) {
53885455
t.Parallel()
53895456

partitioned_query.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"io"
2323

2424
"cloud.google.com/go/spanner"
25+
"github.com/googleapis/go-sql-spanner/parser"
2526
"google.golang.org/grpc/codes"
2627
"google.golang.org/grpc/status"
2728
)
@@ -231,7 +232,7 @@ func (pq *PartitionedQuery) execute(ctx context.Context, index int) (*rows, erro
231232
return nil, spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "invalid partition index: %d", index))
232233
}
233234
spannerIter := pq.tx.Execute(ctx, pq.Partitions[index])
234-
iter := &readOnlyRowIterator{spannerIter}
235+
iter := &readOnlyRowIterator{spannerIter, parser.StatementTypeQuery}
235236
return &rows{it: iter, decodeOption: pq.execOptions.DecodeOption}, nil
236237
}
237238

snippets/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ replace github.com/googleapis/go-sql-spanner => ../
99
require (
1010
cloud.google.com/go/spanner v1.85.1
1111
github.com/docker/docker v28.4.0+incompatible
12-
github.com/googleapis/go-sql-spanner v1.17.0
12+
github.com/googleapis/go-sql-spanner v1.18.0
1313
github.com/testcontainers/testcontainers-go v0.38.0
1414
)
1515

spannerlib/api/rows.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ func (rows *rows) Metadata() (*spannerpb.ResultSetMetadata, error) {
139139

140140
func (rows *rows) ResultSetStats(ctx context.Context) (*spannerpb.ResultSetStats, error) {
141141
if rows.stats == nil {
142-
rows.readStats(ctx)
142+
if err := rows.readStats(ctx); err != nil {
143+
return nil, err
144+
}
143145
}
144146
return rows.stats, nil
145147
}
@@ -171,7 +173,9 @@ func (rows *rows) Next(ctx context.Context) (*structpb.ListValue, error) {
171173
if !ok {
172174
rows.done = true
173175
// No more rows. Read stats and return nil.
174-
rows.readStats(ctx)
176+
if err := rows.readStats(ctx); err != nil {
177+
return nil, err
178+
}
175179
// nil indicates no more rows.
176180
return nil, nil
177181
}
@@ -195,12 +199,20 @@ func (rows *rows) Next(ctx context.Context) (*structpb.ListValue, error) {
195199
return rows.values, nil
196200
}
197201

198-
func (rows *rows) readStats(ctx context.Context) {
202+
func (rows *rows) readStats(ctx context.Context) error {
199203
rows.stats = &spannerpb.ResultSetStats{}
200204
if !rows.backend.NextResultSet() {
201-
return
205+
return status.Error(codes.Internal, "stats results not found")
202206
}
203207
if rows.backend.Next() {
204-
_ = rows.backend.Scan(&rows.stats)
208+
if err := rows.backend.Scan(&rows.stats); err != nil {
209+
return err
210+
}
211+
} else {
212+
if err := rows.backend.Err(); err != nil {
213+
return err
214+
}
215+
return status.Error(codes.Internal, "stats row not found")
205216
}
217+
return nil
206218
}

spannerlib/api/transaction_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func TestBeginAndCommit(t *testing.T) {
5656
t.Fatalf("ResultSetStats returned unexpected error: %v", err)
5757
}
5858
if g, w := stats.GetRowCountExact(), int64(testutil.UpdateBarSetFooRowCount); g != w {
59-
t.Fatalf("row count mismatch\n Got: %v\nWant: %v", g, w)
59+
t.Fatalf("row count mismatch for rows %d:%d:%d\n Got: %v\nWant: %v", poolId, connId, rowsId, g, w)
6060
}
6161
if err := CloseRows(ctx, poolId, connId, rowsId); err != nil {
6262
t.Fatalf("CloseRows returned unexpected error: %v", err)
@@ -125,7 +125,7 @@ func TestBeginAndRollback(t *testing.T) {
125125
t.Fatalf("ResultSetStats returned unexpected error: %v", err)
126126
}
127127
if g, w := stats.GetRowCountExact(), int64(testutil.UpdateBarSetFooRowCount); g != w {
128-
t.Fatalf("row count mismatch\n Got: %v\nWant: %v", g, w)
128+
t.Fatalf("row count mismatch for rows %d:%d:%d\n Got: %v\nWant: %v", poolId, connId, rowsId, g, w)
129129
}
130130
if err := CloseRows(ctx, poolId, connId, rowsId); err != nil {
131131
t.Fatalf("CloseRows returned unexpected error: %v", err)

spannerlib/grpc-server/build-protos.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PATH="${PATH}:${HOME}/go/bin"
2-
rm -rf googleapis/google/spannerlib
3-
cp -r google/spannerlib googleapis/google
2+
rm -rf googleapis/google/spannerlib || true
3+
cp -r google/spannerlib googleapis/google/spannerlib
44
cd googleapis || exit 1
55
protoc \
66
--go_out=../ \

0 commit comments

Comments
 (0)