Skip to content

Commit 013a61c

Browse files
committed
chore: add .NET specification tests
1 parent c7c0792 commit 013a61c

File tree

11 files changed

+55
-43
lines changed

11 files changed

+55
-43
lines changed

checksum_row_iterator.go

Lines changed: 11 additions & 6 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.
@@ -255,8 +257,11 @@ func (it *checksumRowIterator) Metadata() (*sppb.ResultSetMetadata, error) {
255257
func (it *checksumRowIterator) ResultSetStats() *sppb.ResultSetStats {
256258
// TODO: The Spanner client library should offer an option to get the full
257259
// ResultSetStats, instead of only the RowCount and QueryPlan.
258-
return &sppb.ResultSetStats{
259-
RowCount: &sppb.ResultSetStats_RowCountExact{RowCountExact: it.RowIterator.RowCount},
260+
stats := &sppb.ResultSetStats{
260261
QueryPlan: it.RowIterator.QueryPlan,
261262
}
263+
if it.stmtType == parser.StatementTypeDml {
264+
stats.RowCount = &sppb.ResultSetStats_RowCountExact{RowCountExact: it.RowIterator.RowCount}
265+
}
266+
return stats
262267
}

conn.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,13 +888,13 @@ func (c *conn) queryContext(ctx context.Context, query string, execOptions *Exec
888888
// The statement was either detected as being a query, or potentially not recognized at all.
889889
// In that case, just default to using a single-use read-only transaction and let Spanner
890890
// return an error if the statement is not suited for that type of transaction.
891-
iter = &readOnlyRowIterator{c.execSingleQuery(ctx, c.client, stmt, c.ReadOnlyStaleness(), execOptions)}
891+
iter = &readOnlyRowIterator{c.execSingleQuery(ctx, c.client, stmt, c.ReadOnlyStaleness(), execOptions), statementType.StatementType}
892892
}
893893
} else {
894894
if execOptions.PartitionedQueryOptions.PartitionQuery {
895895
return c.tx.partitionQuery(ctx, stmt, execOptions)
896896
}
897-
iter, err = c.tx.Query(ctx, stmt, execOptions)
897+
iter, err = c.tx.Query(ctx, stmt, statementType.StatementType, execOptions)
898898
if err != nil {
899899
return nil, err
900900
}

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

spannerlib/api/rows.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type rows struct {
8888
backend *sql.Rows
8989
metadata *spannerpb.ResultSetMetadata
9090
stats *spannerpb.ResultSetStats
91+
done bool
9192

9293
buffer []any
9394
values *structpb.ListValue
@@ -135,15 +136,16 @@ func (gv *genericValue) Scan(src any) error {
135136

136137
func (rows *rows) Next() (*structpb.ListValue, error) {
137138
// No columns means no rows, so just return nil to indicate that there are no (more) rows.
138-
if len(rows.metadata.RowType.Fields) == 0 {
139+
if len(rows.metadata.RowType.Fields) == 0 || rows.done {
139140
return nil, nil
140141
}
141142
if rows.stats != nil {
142143
return nil, spanner.ToSpannerError(status.Error(codes.FailedPrecondition, "cannot read more data after returning stats"))
143144
}
144145
ok := rows.backend.Next()
145146
if !ok {
146-
// No more rows. Read stats and nil.
147+
rows.done = true
148+
// No more rows. Read stats and return nil.
147149
rows.readStats()
148150
// nil indicates no more rows.
149151
return nil, nil

spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/GrpcLibSpanner.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ await stream.RequestStream.WriteAsync(new ConnectionStreamRequest
9797
public Rows ExecuteTransaction(Transaction transaction, ExecuteSqlRequest statement)
9898
{
9999
var rows = _lib.ExecuteTransaction(ToProto(transaction), statement);
100-
return new Rows(transaction.Connection, rows.Id);
100+
return new Rows(transaction.SpannerConnection, rows.Id);
101101
}
102102

103103
public long[] ExecuteBatch(Connection connection, ExecuteBatchDmlRequest statements)
@@ -123,7 +123,7 @@ public Task<long[]> ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequ
123123

124124
public async Task<ResultSetMetadata?> MetadataAsync(Rows rows)
125125
{
126-
if (_streams.TryGetValue(rows.Connection.Id, out var stream))
126+
if (_streams.TryGetValue(rows.SpannerConnection.Id, out var stream))
127127
{
128128
return await MetadataStreaming(stream, rows);
129129
}
@@ -161,7 +161,7 @@ await stream.RequestStream.WriteAsync(new ConnectionStreamRequest
161161

162162
public async Task<ListValue?> NextAsync(Rows rows, int numRows, ISpanner.RowEncoding encoding)
163163
{
164-
if (_streams.TryGetValue(rows.Connection.Id, out var stream))
164+
if (_streams.TryGetValue(rows.SpannerConnection.Id, out var stream))
165165
{
166166
return await NextStreaming(stream, rows);
167167
}
@@ -213,7 +213,7 @@ public void Rollback(Connection connection)
213213

214214
private static V1.Connection ToProto(Connection connection) => new() {Pool = ToProto(connection.Pool), Id = connection.Id};
215215

216-
private static V1.Transaction ToProto(Transaction transaction) => new() {Connection = ToProto(transaction.Connection), Id = transaction.Id};
216+
private static V1.Transaction ToProto(Transaction transaction) => new() {Connection = ToProto(transaction.SpannerConnection), Id = transaction.Id};
217217

218-
private static V1.Rows ToProto(Rows rows) => new() {Connection = ToProto(rows.Connection), Id = rows.Id};
218+
private static V1.Rows ToProto(Rows rows) => new() {Connection = ToProto(rows.SpannerConnection), Id = rows.Id};
219219
}

spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Rows.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ namespace Google.Cloud.SpannerLib;
88

99
public class Rows : AbstractLibObject
1010
{
11-
private Lazy<ResultSetStats?> _stats;
11+
private readonly Lazy<ResultSetStats?> _stats;
1212

13-
internal Connection Connection { get; private set; }
13+
internal Connection SpannerConnection { get; private set; }
1414

1515
private readonly AsyncServerStreamingCall<ResultSet>? _stream;
1616

@@ -39,13 +39,9 @@ public long UpdateCount
3939
}
4040
}
4141

42-
internal Rows(Connection connection, long id) : this(connection, id, true)
42+
internal Rows(Connection connection, long id, bool initMetadata = true) : base(connection.Spanner, id)
4343
{
44-
}
45-
46-
internal Rows(Connection connection, long id, bool initMetadata) : base(connection.Spanner, id)
47-
{
48-
Connection = connection;
44+
SpannerConnection = connection;
4945
if (initMetadata)
5046
{
5147
Metadata = Spanner.Metadata(this);
@@ -56,7 +52,7 @@ internal Rows(Connection connection, long id, bool initMetadata) : base(connecti
5652

5753
internal Rows(Connection connection, AsyncServerStreamingCall<ResultSet> stream) : base(connection.Spanner, -1)
5854
{
59-
Connection = connection;
55+
SpannerConnection = connection;
6056
_stats = new(() => Spanner.Stats(this));
6157
_stream = stream;
6258
}

spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SharedLibSpanner.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public Task<long[]> ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequ
119119

120120
public ResultSetMetadata? Metadata(Rows rows)
121121
{
122-
using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Metadata(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id));
122+
using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Metadata(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id));
123123
return handler.Length == 0 ? null : ResultSetMetadata.Parser.ParseFrom(handler.Value());
124124
}
125125

@@ -130,13 +130,13 @@ public Task<long[]> ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequ
130130

131131
public ResultSetStats? Stats(Rows rows)
132132
{
133-
using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.ResultSetStats(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id));
133+
using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.ResultSetStats(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id));
134134
return handler.Length == 0 ? null : ResultSetStats.Parser.ParseFrom(handler.Value());
135135
}
136136

137137
public ListValue? Next(Rows rows, int numRows, ISpanner.RowEncoding encoding)
138138
{
139-
using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Next(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id, numRows, (int) encoding));
139+
using var handler = ExecuteLibraryFunction(() => Native.SpannerLib.Next(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id, numRows, (int) encoding));
140140
return handler.Length == 0 ? null : ListValue.Parser.ParseFrom(handler.Value());
141141
}
142142

@@ -147,7 +147,7 @@ public Task<long[]> ExecuteBatchAsync(Connection connection, ExecuteBatchDmlRequ
147147

148148
public void CloseRows(Rows rows)
149149
{
150-
ExecuteAndReleaseLibraryFunction(() => Native.SpannerLib.CloseRows(rows.Connection.Pool.Id, rows.Connection.Id, rows.Id));
150+
ExecuteAndReleaseLibraryFunction(() => Native.SpannerLib.CloseRows(rows.SpannerConnection.Pool.Id, rows.SpannerConnection.Id, rows.Id));
151151
}
152152

153153
public Transaction BeginTransaction(Connection connection, TransactionOptions transactionOptions)

spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/SpannerException.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using System.Data;
2+
using System.Data.Common;
23

34
namespace Google.Cloud.SpannerLib
45
{
56

6-
public class SpannerException : DataException
7+
public class SpannerException : DbException
78
{
89
/// <summary>
910
/// An error code that indicates the general class of problem.

spannerlib/dotnet-spannerlib/Google.Cloud.SpannerLib/Transaction.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,35 @@ namespace Google.Cloud.SpannerLib;
55

66
public class Transaction : AbstractLibObject
77
{
8-
internal Connection Connection { get; private set; }
8+
internal Connection SpannerConnection { get; private set; }
99

1010
internal Transaction(Connection connection, long id) : base(connection.Spanner, id)
1111
{
12-
Connection = connection;
12+
SpannerConnection = connection;
1313
}
1414

1515
public Rows Execute(ExecuteSqlRequest statement)
1616
{
17-
return Spanner.Execute(Connection, statement);
17+
return Spanner.Execute(SpannerConnection, statement);
1818
}
1919

2020
public CommitResponse Commit()
2121
{
2222
MarkDisposed();
23-
return Spanner.Commit(Connection);
23+
return Spanner.Commit(SpannerConnection);
2424
}
2525

2626
public void Rollback()
2727
{
2828
MarkDisposed();
29-
Spanner.Rollback(Connection);
29+
Spanner.Rollback(SpannerConnection);
3030
}
3131

3232
protected override void CloseLibObject()
3333
{
3434
try
3535
{
36-
Spanner.Rollback(Connection);
36+
Spanner.Rollback(SpannerConnection);
3737
}
3838
catch (Exception)
3939
{

transaction.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type contextTransaction interface {
4444
Commit() error
4545
Rollback() error
4646
resetForRetry(ctx context.Context) error
47-
Query(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (rowIterator, error)
47+
Query(ctx context.Context, stmt spanner.Statement, stmtType parser.StatementType, execOptions *ExecOptions) (rowIterator, error)
4848
partitionQuery(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (driver.Rows, error)
4949
ExecContext(ctx context.Context, stmt spanner.Statement, statementInfo *parser.StatementInfo, options spanner.QueryOptions) (*result, error)
5050

@@ -67,6 +67,7 @@ var _ rowIterator = &readOnlyRowIterator{}
6767

6868
type readOnlyRowIterator struct {
6969
*spanner.RowIterator
70+
stmtType parser.StatementType
7071
}
7172

7273
func (ri *readOnlyRowIterator) Next() (*spanner.Row, error) {
@@ -84,10 +85,13 @@ func (ri *readOnlyRowIterator) Metadata() (*sppb.ResultSetMetadata, error) {
8485
func (ri *readOnlyRowIterator) ResultSetStats() *sppb.ResultSetStats {
8586
// TODO: The Spanner client library should offer an option to get the full
8687
// ResultSetStats, instead of only the RowCount and QueryPlan.
87-
return &sppb.ResultSetStats{
88-
RowCount: &sppb.ResultSetStats_RowCountExact{RowCountExact: ri.RowIterator.RowCount},
88+
stats := &sppb.ResultSetStats{
8989
QueryPlan: ri.RowIterator.QueryPlan,
9090
}
91+
if ri.stmtType == parser.StatementTypeDml {
92+
stats.RowCount = &sppb.ResultSetStats_RowCountExact{RowCountExact: ri.RowIterator.RowCount}
93+
}
94+
return stats
9195
}
9296

9397
type txResult int
@@ -135,7 +139,7 @@ func (tx *readOnlyTransaction) resetForRetry(ctx context.Context) error {
135139
return nil
136140
}
137141

138-
func (tx *readOnlyTransaction) Query(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (rowIterator, error) {
142+
func (tx *readOnlyTransaction) Query(ctx context.Context, stmt spanner.Statement, stmtType parser.StatementType, execOptions *ExecOptions) (rowIterator, error) {
139143
tx.logger.DebugContext(ctx, "Query", "stmt", stmt.SQL)
140144
if execOptions.PartitionedQueryOptions.AutoPartitionQuery {
141145
if tx.boTx == nil {
@@ -152,7 +156,7 @@ func (tx *readOnlyTransaction) Query(ctx context.Context, stmt spanner.Statement
152156
}
153157
return mi, nil
154158
}
155-
return &readOnlyRowIterator{tx.roTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions)}, nil
159+
return &readOnlyRowIterator{tx.roTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions), stmtType}, nil
156160
}
157161

158162
func (tx *readOnlyTransaction) partitionQuery(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (driver.Rows, error) {
@@ -456,7 +460,7 @@ func (tx *readWriteTransaction) resetForRetry(ctx context.Context) error {
456460
// Query executes a query using the read/write transaction and returns a
457461
// rowIterator that will automatically retry the read/write transaction if the
458462
// transaction is aborted during the query or while iterating the returned rows.
459-
func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statement, execOptions *ExecOptions) (rowIterator, error) {
463+
func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statement, stmtType parser.StatementType, execOptions *ExecOptions) (rowIterator, error) {
460464
tx.logger.Debug("Query", "stmt", stmt.SQL)
461465
tx.active = true
462466
if err := tx.maybeRunAutoDmlBatch(ctx); err != nil {
@@ -465,7 +469,7 @@ func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statemen
465469
// If internal retries have been disabled, we don't need to keep track of a
466470
// running checksum for all results that we have seen.
467471
if !tx.retryAborts() {
468-
return &readOnlyRowIterator{tx.rwTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions)}, nil
472+
return &readOnlyRowIterator{tx.rwTx.QueryWithOptions(ctx, stmt, execOptions.QueryOptions), stmtType}, nil
469473
}
470474

471475
// If retries are enabled, we need to use a row iterator that will keep
@@ -476,6 +480,7 @@ func (tx *readWriteTransaction) Query(ctx context.Context, stmt spanner.Statemen
476480
ctx: ctx,
477481
tx: tx,
478482
stmt: stmt,
483+
stmtType: stmtType,
479484
options: execOptions.QueryOptions,
480485
buffer: buffer,
481486
enc: gob.NewEncoder(buffer),

0 commit comments

Comments
 (0)