Skip to content

Commit be34e51

Browse files
committed
Merge branch 'main' into ado-net-driver
2 parents 3879a02 + 8638178 commit be34e51

File tree

7 files changed

+172
-15
lines changed

7 files changed

+172
-15
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.21.0"
2+
".": "1.22.0"
33
}

CHANGES.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
# Changelog
22

3+
## [1.22.0](https://github.com/googleapis/go-sql-spanner/compare/v1.21.0...v1.22.0) (2025-12-15)
4+
5+
6+
### Features
7+
8+
* Add Split function to parser ([#634](https://github.com/googleapis/go-sql-spanner/issues/634)) ([302e860](https://github.com/googleapis/go-sql-spanner/commit/302e860f6858f24bf044a6d42b6a96f9c38706d5))
9+
* Add support for multi-sql using next_result_set ([#676](https://github.com/googleapis/go-sql-spanner/issues/676)) ([8d068d1](https://github.com/googleapis/go-sql-spanner/commit/8d068d10865bd806548eff4a78fca2f8ae4d2845))
10+
* Add support for statement-scoped connection state ([#599](https://github.com/googleapis/go-sql-spanner/issues/599)) ([5a158c3](https://github.com/googleapis/go-sql-spanner/commit/5a158c3c650103718fcd496dc06868ec04339e1e))
11+
* Add TimestampBound option to ExecOptions ([#522](https://github.com/googleapis/go-sql-spanner/issues/522)) ([fc78709](https://github.com/googleapis/go-sql-spanner/commit/fc78709445a6867138dc3b2bbddf06397640acbc))
12+
* Support CREATE DATABASE with extra statements ([#629](https://github.com/googleapis/go-sql-spanner/issues/629)) ([e09de0b](https://github.com/googleapis/go-sql-spanner/commit/e09de0ba83f878ded761e82d5b0403745a4c3c66))
13+
* Support multi-statement SQL strings in QueryContext ([#638](https://github.com/googleapis/go-sql-spanner/issues/638)) ([019442f](https://github.com/googleapis/go-sql-spanner/commit/019442f334ad44b04cc29734934c8dc42632a8fa))
14+
* Support statement_timeout and transaction_timeout property ([#578](https://github.com/googleapis/go-sql-spanner/issues/578)) ([b542646](https://github.com/googleapis/go-sql-spanner/commit/b542646528eeff4a11e27b8c4232492392d97aed))
15+
16+
17+
### Bug Fixes
18+
19+
* Backslash at end of string was misinterpreted ([#651](https://github.com/googleapis/go-sql-spanner/issues/651)) ([fc2fdd5](https://github.com/googleapis/go-sql-spanner/commit/fc2fdd53b913d9626f9f843bb533d903069de561))
20+
* Close iterator on error ([#644](https://github.com/googleapis/go-sql-spanner/issues/644)) ([c0023ee](https://github.com/googleapis/go-sql-spanner/commit/c0023eebdbd45b566b431bf16e325ba0de98c7cd))
21+
* Dollar-quote tags may not contain whitespaces ([#606](https://github.com/googleapis/go-sql-spanner/issues/606)) ([db609ec](https://github.com/googleapis/go-sql-spanner/commit/db609ec8dc1d8427dfd1f76fd1bc85ca782be034))
22+
* Support e-strings for PostgreSQL ([#607](https://github.com/googleapis/go-sql-spanner/issues/607)) ([d050b1c](https://github.com/googleapis/go-sql-spanner/commit/d050b1c37f021b6eb94ff9112d104674360b14a8))
23+
* Update all dependencies ([#588](https://github.com/googleapis/go-sql-spanner/issues/588)) ([2e97f4c](https://github.com/googleapis/go-sql-spanner/commit/2e97f4c8aaeca2deec5fb669f3b47644e9eac4d8))
24+
* Update all dependencies ([#639](https://github.com/googleapis/go-sql-spanner/issues/639)) ([2311ffb](https://github.com/googleapis/go-sql-spanner/commit/2311ffbd367afc089c14955c3331f18236454a1e))
25+
* Update all dependencies ([#646](https://github.com/googleapis/go-sql-spanner/issues/646)) ([b9dfc57](https://github.com/googleapis/go-sql-spanner/commit/b9dfc57ea35af5e9ce44e0a34dcfff31c9469a93))
26+
* Update all dependencies ([#692](https://github.com/googleapis/go-sql-spanner/issues/692)) ([af69259](https://github.com/googleapis/go-sql-spanner/commit/af6925933d06510ae1c2c1b47f3dbdc655559e74))
27+
* Update dependency io.grpc:grpc-bom to v1.77.0 ([#640](https://github.com/googleapis/go-sql-spanner/issues/640)) ([a7cab7e](https://github.com/googleapis/go-sql-spanner/commit/a7cab7e656b35a3271142a69b00b1af482d5b157))
28+
* Update dependency io.netty:netty-transport-native-epoll to v4.2.8.final ([#694](https://github.com/googleapis/go-sql-spanner/issues/694)) ([4ab3748](https://github.com/googleapis/go-sql-spanner/commit/4ab374854b5faeba8184dfe27004c0541923febd))
29+
* Update module github.com/testcontainers/testcontainers-go to v0.40.0 ([#602](https://github.com/googleapis/go-sql-spanner/issues/602)) ([822b5a4](https://github.com/googleapis/go-sql-spanner/commit/822b5a4286e78e82b7e7de6de0237b231636851c))
30+
* Update protobuf monorepo ([#633](https://github.com/googleapis/go-sql-spanner/issues/633)) ([21cbfa9](https://github.com/googleapis/go-sql-spanner/commit/21cbfa9ae79a54dacaff9ee06390d2ac3138c6fc))
31+
* Update protobuf monorepo ([#671](https://github.com/googleapis/go-sql-spanner/issues/671)) ([d8fba2d](https://github.com/googleapis/go-sql-spanner/commit/d8fba2dc1379b7ab1d211686b89f9fb9e3215029))
32+
33+
34+
### Performance Improvements
35+
36+
* Improve checksum calculation ([#688](https://github.com/googleapis/go-sql-spanner/issues/688)) ([6fb5cc8](https://github.com/googleapis/go-sql-spanner/commit/6fb5cc8f03b9d7d5c096fc725e101676b887c613))
37+
38+
39+
### Documentation
40+
41+
* Update spannerlib-python documentation ([#685](https://github.com/googleapis/go-sql-spanner/issues/685)) ([30e6339](https://github.com/googleapis/go-sql-spanner/commit/30e6339ecd3236a5642ce1070348232c2c3f1ff8))
42+
* Update spannerlmockserver documentation ([#684](https://github.com/googleapis/go-sql-spanner/issues/684)) ([22aa6eb](https://github.com/googleapis/go-sql-spanner/commit/22aa6eb5d42ba0fa01c63c4ede66e6c0a89e9467))
43+
344
## [1.21.0](https://github.com/googleapis/go-sql-spanner/compare/v1.20.0...v1.21.0) (2025-10-29)
445

546

driver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import (
5151
"google.golang.org/protobuf/proto"
5252
)
5353

54-
const userAgent = "go-sql-spanner/1.21.0" // x-release-please-version
54+
const userAgent = "go-sql-spanner/1.22.0" // x-release-please-version
5555

5656
const gormModule = "github.com/googleapis/go-gorm-spanner"
5757
const gormUserAgent = "go-gorm-spanner"

spannerlib/wrappers/spannerlib-dotnet/spannerlib-dotnet-tests/RowsTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System.Text;
1516
using Google.Cloud.Spanner.Admin.Database.V1;
1617
using Google.Cloud.Spanner.V1;
1718
using Google.Cloud.SpannerLib.MockServer;
@@ -83,6 +84,50 @@ public void TestRandomResults([Values] LibType libType, [Values(0, 1, 10)] int n
8384
var request = Fixture.SpannerMock.Requests.OfType<ExecuteSqlRequest>().First();
8485
Assert.That(request.Transaction?.SingleUse?.ReadOnly?.HasStrong ?? false);
8586
}
87+
88+
[Test]
89+
public async Task TestCancellation([Values] LibType libType, [Values(0, 1, 10)] int numRows, [Values(0, 1, 5, 9, 10, 11)] int prefetchRows)
90+
{
91+
var rowType = RandomResultSetGenerator.GenerateAllTypesRowType();
92+
var results = RandomResultSetGenerator.Generate(rowType, numRows);
93+
Fixture.SpannerMock.AddOrUpdateStatementResult("select * from random", StatementResult.CreateQuery(results));
94+
95+
CancellationTokenSource cts = new CancellationTokenSource();
96+
var numReadRowsBeforeCancellation = Random.Shared.Next(numRows);
97+
await using var pool = Pool.Create(SpannerLibDictionary[libType], ConnectionString);
98+
await using var connection = pool.CreateConnection();
99+
if (numReadRowsBeforeCancellation == 0)
100+
{
101+
await cts.CancelAsync();
102+
var exception = Assert.CatchAsync<Exception>(async () => await connection.ExecuteAsync(new ExecuteSqlRequest { Sql = "select * from random" }, prefetchRows, cts.Token));
103+
Assert.That(exception, Is.Not.Null);
104+
Assert.That(exception, Is.InstanceOf<OperationCanceledException>().Or.InstanceOf<RpcException>().Or.InstanceOf<TaskCanceledException>());
105+
}
106+
else
107+
{
108+
await using var rows = await connection.ExecuteAsync(new ExecuteSqlRequest { Sql = "select * from random" },
109+
prefetchRows, cts.Token);
110+
111+
var rowCount = 0;
112+
while (await rows.NextAsync(cts.Token) is not null)
113+
{
114+
rowCount++;
115+
if (numReadRowsBeforeCancellation == rowCount)
116+
{
117+
await cts.CancelAsync();
118+
var exception = Assert.CatchAsync<Exception>(async () => await rows.NextAsync(cts.Token));
119+
Assert.That(exception, Is.Not.Null);
120+
Assert.That(exception, Is.InstanceOf<OperationCanceledException>().Or.InstanceOf<RpcException>().Or.InstanceOf<TaskCanceledException>());
121+
break;
122+
}
123+
}
124+
Assert.That(rowCount, Is.LessThan(numRows));
125+
126+
Assert.That(Fixture.SpannerMock.Requests.OfType<ExecuteSqlRequest>().Count(), Is.EqualTo(1));
127+
var request = Fixture.SpannerMock.Requests.OfType<ExecuteSqlRequest>().First();
128+
Assert.That(request.Transaction?.SingleUse?.ReadOnly?.HasStrong ?? false);
129+
}
130+
}
86131

87132
[Test]
88133
public void TestLargeStringValue([Values] LibType libType)
@@ -385,6 +430,51 @@ public async Task TestMultipleMixedStatements([Values] LibType libType, [Values(
385430
Assert.That(numResultSets, Is.EqualTo(5));
386431
}
387432

433+
[Test]
434+
public async Task TestGetAllUpdateCounts([Values] LibType libType, [Values(0, 1, 2, 5)] int numDmlStatements, [Values(2, 10)] int numRows, [Values(0, 1, 2, 3, 5, 9, 10, 11)] int prefetchRows, [Values] bool async)
435+
{
436+
var updateCount = 3L;
437+
var dml = "update my_table set value=1 where id in (1,2,3)";
438+
Fixture.SpannerMock.AddOrUpdateStatementResult(dml, StatementResult.CreateUpdateCount(updateCount));
439+
Fixture.SpannerMock.AddOrUpdateStatementResult(dml + ";", StatementResult.CreateUpdateCount(updateCount));
440+
441+
var rowType = RandomResultSetGenerator.GenerateAllTypesRowType();
442+
var results = RandomResultSetGenerator.Generate(rowType, numRows);
443+
var query = "select * from random";
444+
Fixture.SpannerMock.AddOrUpdateStatementResult(query, StatementResult.CreateQuery(results));
445+
446+
// Create a SQL string containing a mix of DML and queries.
447+
var builder = new StringBuilder();
448+
for (var i = 0; i < numDmlStatements; i++)
449+
{
450+
while (Random.Shared.Next(2) == 0)
451+
{
452+
builder.Append(query).Append(';');
453+
}
454+
builder.Append(dml).Append(';');
455+
while (Random.Shared.Next(5) == 0)
456+
{
457+
builder.Append(query).Append(';');
458+
}
459+
}
460+
var sql = builder.ToString();
461+
if (string.IsNullOrEmpty(sql))
462+
{
463+
sql = query;
464+
}
465+
466+
await using var pool = Pool.Create(SpannerLibDictionary[libType], ConnectionString);
467+
await using var connection = pool.CreateConnection();
468+
await using var rows = async
469+
? await connection.ExecuteAsync(new ExecuteSqlRequest { Sql = sql }, prefetchRows)
470+
// ReSharper disable once MethodHasAsyncOverload
471+
: connection.Execute(new ExecuteSqlRequest { Sql = sql }, prefetchRows);
472+
473+
// ReSharper disable once MethodHasAsyncOverload
474+
var totalUpdateCount = async ? await rows.GetTotalUpdateCountAsync() : rows.GetTotalUpdateCount();
475+
Assert.That(totalUpdateCount, Is.EqualTo(numDmlStatements == 0 ? -1 : updateCount * numDmlStatements));
476+
}
477+
388478
[Test]
389479
public async Task TestMultipleMixedStatementsWithErrors(
390480
[Values] LibType libType,

spannerlib/wrappers/spannerlib-dotnet/spannerlib-dotnet/Rows.cs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,58 @@ public Rows(Connection connection, long id, bool initMetadata = true) : base(con
106106
/// </summary>
107107
/// <returns>
108108
/// The total update count of all the result sets in this Rows object.
109+
/// If the SQL string only contained non-DML statements, then the update count is -1. If the SQL string contained at
110+
/// least one DML statement, then the returned value is the sum of all update counts of the DML statements in the
111+
/// SQL string that was executed.
109112
/// </returns>
110113
public long GetTotalUpdateCount()
111114
{
112-
var result = UpdateCount;
113-
while (NextResultSet())
115+
long result = -1;
116+
var hasUpdateCount = false;
117+
do
114118
{
115-
result += UpdateCount;
116-
}
119+
var updateCount = UpdateCount;
120+
if (updateCount > -1)
121+
{
122+
if (!hasUpdateCount)
123+
{
124+
hasUpdateCount = true;
125+
result = 0;
126+
}
127+
result += updateCount;
128+
}
129+
} while (NextResultSet());
117130
return result;
118131
}
119-
132+
120133
/// <summary>
121134
/// Gets the total update count in this Rows object. This consumes all data in all the result sets.
122135
/// This method should only be called if the caller is only interested in the update count, and not in any of the
123136
/// rows in the result sets.
124137
/// </summary>
125138
/// <returns>
126139
/// The total update count of all the result sets in this Rows object.
140+
/// If the SQL string only contained non-DML statements, then the update count is -1. If the SQL string contained at
141+
/// least one DML statement, then the returned value is the sum of all update counts of the DML statements in the
142+
/// SQL string that was executed.
127143
/// </returns>
128144
public async Task<long> GetTotalUpdateCountAsync(CancellationToken cancellationToken = default)
129145
{
130-
var result = UpdateCount;
131-
while (await NextResultSetAsync(cancellationToken).ConfigureAwait(false))
146+
long result = -1;
147+
var hasUpdateCount = false;
148+
do
132149
{
133-
result += UpdateCount;
134-
}
150+
var updateCount = UpdateCount;
151+
if (updateCount > -1)
152+
{
153+
if (!hasUpdateCount)
154+
{
155+
hasUpdateCount = true;
156+
result = 0;
157+
}
158+
result += updateCount;
159+
}
160+
} while (await NextResultSetAsync(cancellationToken).ConfigureAwait(false));
135161
return result;
136162
}
137163

stmt_with_mockserver_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func TestStatementTimeout(t *testing.T) {
191191
ctx := context.Background()
192192

193193
// The database/sql driver uses ExecuteStreamingSql for all statements.
194-
server.TestSpanner.PutExecutionTime(testutil.MethodExecuteStreamingSql, testutil.SimulatedExecutionTime{MinimumExecutionTime: 10 * time.Millisecond})
194+
server.TestSpanner.PutExecutionTime(testutil.MethodExecuteStreamingSql, testutil.SimulatedExecutionTime{MinimumExecutionTime: 50 * time.Millisecond})
195195

196196
_, err := db.ExecContext(ctx, testutil.UpdateBarSetFoo)
197197
if g, w := spanner.ErrCode(err), codes.DeadlineExceeded; g != w {

transaction_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ func TestTransactionTimeout(t *testing.T) {
325325
defer teardown()
326326
ctx := context.Background()
327327

328-
server.TestSpanner.PutExecutionTime(testutil.MethodExecuteStreamingSql, testutil.SimulatedExecutionTime{MinimumExecutionTime: 20 * time.Millisecond})
328+
server.TestSpanner.PutExecutionTime(testutil.MethodExecuteStreamingSql, testutil.SimulatedExecutionTime{MinimumExecutionTime: 50 * time.Millisecond})
329329
tx, _ := db.BeginTx(ctx, &sql.TxOptions{})
330330
if _, err := tx.ExecContext(ctx, "set local transaction_timeout=10ms"); err != nil {
331331
t.Fatal(err)
@@ -367,14 +367,14 @@ func TestTransactionTimeoutSecondStatement(t *testing.T) {
367367
ctx := context.Background()
368368

369369
tx, _ := db.BeginTx(ctx, &sql.TxOptions{})
370-
if _, err := tx.ExecContext(ctx, "set local transaction_timeout=20ms"); err != nil {
370+
if _, err := tx.ExecContext(ctx, "set local transaction_timeout=40ms"); err != nil {
371371
t.Fatal(err)
372372
}
373373
if _, err := tx.ExecContext(ctx, testutil.UpdateBarSetFoo); err != nil {
374374
t.Fatal(err)
375375
}
376376

377-
server.TestSpanner.PutExecutionTime(testutil.MethodExecuteStreamingSql, testutil.SimulatedExecutionTime{MinimumExecutionTime: 50 * time.Millisecond})
377+
server.TestSpanner.PutExecutionTime(testutil.MethodExecuteStreamingSql, testutil.SimulatedExecutionTime{MinimumExecutionTime: 60 * time.Millisecond})
378378
rows, err := tx.QueryContext(ctx, testutil.SelectFooFromBar, ExecOptions{DirectExecuteQuery: true})
379379
if rows != nil {
380380
_ = rows.Close()

0 commit comments

Comments
 (0)