Skip to content

Commit dda2e1d

Browse files
feat: support PostgreSQL isolation level statements (#3706)
* feat: add default_isolation_level connection property Add a `default_isolation_level` property for the Connection API. This property will be used by the JDBC driver and PGAdapter to set a default isolation level for all read/write transactions that are executed by a connection. Support for setting an isolation level for a single transaction will be added in a follow-up pull request. * feat: specify isolation level per transaction Add an option to specify the isolation level for a single transaction. This isolation level overrides the current default isolation level that has been set for the connection. This option only has an effect for read/write transactions. * feat: support PostgreSQL isolation level statements Adds support for PostgreSQL-dialect SQL statements for setting the isolation level of a transaction. * chore: generate libraries at Fri Mar 28 11:51:13 UTC 2025 --------- Co-authored-by: cloud-java-bot <[email protected]>
1 parent 868f30f commit dda2e1d

14 files changed

+14639
-8004
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies:
4141
<dependency>
4242
<groupId>com.google.cloud</groupId>
4343
<artifactId>google-cloud-spanner</artifactId>
44-
<version>6.84.0</version>
44+
<version>6.89.0</version>
4545
</dependency>
4646

4747
```

Diff for: google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java

+5
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,11 @@ public PgTransactionMode convert(String value) {
562562
} else if (valueWithSingleSpaces.substring(currentIndex).startsWith("read write")) {
563563
currentIndex += "read write".length();
564564
mode.setAccessMode(AccessMode.READ_WRITE_TRANSACTION);
565+
} else if (valueWithSingleSpaces
566+
.substring(currentIndex)
567+
.startsWith("isolation level repeatable read")) {
568+
currentIndex += "isolation level repeatable read".length();
569+
mode.setIsolationLevel(IsolationLevel.ISOLATION_LEVEL_REPEATABLE_READ);
565570
} else if (valueWithSingleSpaces
566571
.substring(currentIndex)
567572
.startsWith("isolation level serializable")) {

Diff for: google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java

+17-2
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,14 @@ public StatementResult statementBeginTransaction(
456456

457457
@Override
458458
public StatementResult statementBeginPgTransaction(@Nullable PgTransactionMode transactionMode) {
459-
getConnection().beginTransaction();
459+
if (transactionMode == null
460+
|| transactionMode.getIsolationLevel() == null
461+
|| transactionMode.getIsolationLevel() == IsolationLevel.ISOLATION_LEVEL_DEFAULT) {
462+
getConnection().beginTransaction();
463+
} else {
464+
getConnection()
465+
.beginTransaction(transactionMode.getIsolationLevel().getSpannerIsolationLevel());
466+
}
460467
if (transactionMode != null) {
461468
statementSetPgTransactionMode(transactionMode);
462469
}
@@ -501,6 +508,10 @@ public StatementResult statementSetPgTransactionMode(PgTransactionMode transacti
501508
@Override
502509
public StatementResult statementSetPgSessionCharacteristicsTransactionMode(
503510
PgTransactionMode transactionMode) {
511+
if (transactionMode.getIsolationLevel() != null) {
512+
getConnection()
513+
.setDefaultIsolationLevel(transactionMode.getIsolationLevel().getSpannerIsolationLevel());
514+
}
504515
if (transactionMode.getAccessMode() != null) {
505516
switch (transactionMode.getAccessMode()) {
506517
case READ_ONLY_TRANSACTION:
@@ -518,7 +529,11 @@ public StatementResult statementSetPgSessionCharacteristicsTransactionMode(
518529

519530
@Override
520531
public StatementResult statementSetPgDefaultTransactionIsolation(IsolationLevel isolationLevel) {
521-
// no-op
532+
getConnection()
533+
.setDefaultIsolationLevel(
534+
isolationLevel == null
535+
? TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED
536+
: isolationLevel.getSpannerIsolationLevel());
522537
return noResult(SET_DEFAULT_TRANSACTION_ISOLATION);
523538
}
524539

Diff for: google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/PgTransactionMode.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.google.cloud.spanner.connection;
1818

19+
import com.google.spanner.v1.TransactionOptions;
20+
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
1921
import java.util.Objects;
2022

2123
/**
@@ -40,15 +42,30 @@ public String toString() {
4042
}
4143

4244
enum IsolationLevel {
43-
ISOLATION_LEVEL_DEFAULT("ISOLATION LEVEL DEFAULT", "DEFAULT"),
44-
ISOLATION_LEVEL_SERIALIZABLE("ISOLATION LEVEL SERIALIZABLE", "SERIALIZABLE");
45+
ISOLATION_LEVEL_DEFAULT(
46+
"ISOLATION LEVEL DEFAULT",
47+
"DEFAULT",
48+
TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED),
49+
ISOLATION_LEVEL_SERIALIZABLE(
50+
"ISOLATION LEVEL SERIALIZABLE",
51+
"SERIALIZABLE",
52+
TransactionOptions.IsolationLevel.SERIALIZABLE),
53+
ISOLATION_LEVEL_REPEATABLE_READ(
54+
"ISOLATION LEVEL REPEATABLE READ",
55+
"REPEATABLE READ",
56+
TransactionOptions.IsolationLevel.REPEATABLE_READ);
4557

4658
private final String statementString;
4759
private final String shortStatementString;
60+
private final TransactionOptions.IsolationLevel spannerIsolationLevel;
4861

49-
IsolationLevel(String statement, String shortStatementString) {
62+
IsolationLevel(
63+
String statement,
64+
String shortStatementString,
65+
TransactionOptions.IsolationLevel spannerIsolationLevel) {
5066
this.statementString = statement;
5167
this.shortStatementString = shortStatementString;
68+
this.spannerIsolationLevel = spannerIsolationLevel;
5269
}
5370

5471
/**
@@ -67,6 +84,10 @@ public String getShortStatementString() {
6784
return shortStatementString;
6885
}
6986

87+
public TransactionOptions.IsolationLevel getSpannerIsolationLevel() {
88+
return spannerIsolationLevel;
89+
}
90+
7091
@Override
7192
public String toString() {
7293
return statementString;

Diff for: google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json

+20-11
Original file line numberDiff line numberDiff line change
@@ -267,21 +267,24 @@
267267
"exampleStatements": []
268268
},
269269
{
270-
"name": "{START | BEGIN} [TRANSACTION | WORK] [{ (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE))] [[,] NOT DEFERRABLE]}]",
270+
"name": "{START | BEGIN} [TRANSACTION | WORK] [{ (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE|REPEATABLE READ))] [[,] NOT DEFERRABLE]}]",
271271
"executorName": "ClientSideStatementPgBeginExecutor",
272272
"resultType": "NO_RESULT",
273273
"statementType": "BEGIN",
274-
"regex": "(?is)\\A\\s*(?:begin|start)(?:\\s+transaction|\\s+work)?((?:(?:\\s+|\\s*,\\s*)read\\s+only|(?:\\s+|\\s*,\\s*)read\\s+write|(?:\\s+|\\s*,\\s*)isolation\\s+level\\s+default|(?:\\s+|\\s*,\\s*)isolation\\s+level\\s+serializable|(?:\\s+|\\s*,\\s*)not\\s+deferrable)*)?\\s*\\z",
274+
"regex": "(?is)\\A\\s*(?:begin|start)(?:\\s+transaction|\\s+work)?((?:(?:\\s+|\\s*,\\s*)read\\s+only|(?:\\s+|\\s*,\\s*)read\\s+write|(?:\\s+|\\s*,\\s*)isolation\\s+level\\s+default|(?:\\s+|\\s*,\\s*)isolation\\s+level\\s+serializable|(?:\\s+|\\s*,\\s*)isolation\\s+level\\s+repeatable\\s+read|(?:\\s+|\\s*,\\s*)not\\s+deferrable)*)?\\s*\\z",
275275
"method": "statementBeginPgTransaction",
276276
"exampleStatements": [
277277
"begin", "start", "begin transaction", "start transaction", "begin work", "start work",
278278
"begin read only", "start read only", "begin transaction read only", "start transaction read only", "begin work read only", "start work read only",
279279
"begin read write", "start read write", "begin transaction read write", "start transaction read write", "begin work read write", "start work read write",
280280
"begin isolation level default", "start isolation level default", "begin transaction isolation level default", "start transaction isolation level default", "begin work isolation level default", "start work isolation level default",
281281
"begin isolation level serializable", "start isolation level serializable", "begin transaction isolation level serializable", "start transaction isolation level serializable", "begin work isolation level serializable", "start work isolation level serializable",
282+
"begin isolation level repeatable read", "start isolation level repeatable read", "begin transaction isolation level repeatable read", "start transaction isolation level repeatable read", "begin work isolation level repeatable read", "start work isolation level repeatable read",
282283
"begin isolation level default read write", "start isolation level default read only", "begin transaction isolation level default read only", "start transaction isolation level default read write", "begin work isolation level default read write", "start work isolation level default read only",
283284
"begin isolation level serializable read write", "start isolation level serializable read write", "begin transaction isolation level serializable read only", "start transaction isolation level serializable read write", "begin work isolation level serializable read write", "start work isolation level serializable read only",
285+
"begin isolation level repeatable read read write", "start isolation level repeatable read read write", "begin transaction isolation level repeatable read read only", "start transaction isolation level repeatable read read write", "begin work isolation level repeatable read read write", "start work isolation level repeatable read read only",
284286
"begin isolation level serializable, read write", "start isolation level serializable, read write", "begin transaction isolation level serializable, read only", "start transaction isolation level serializable, read write", "begin work isolation level serializable, read write", "start work isolation level serializable, read only",
287+
"begin isolation level repeatable read, read write", "start isolation level repeatable read, read write", "begin transaction isolation level repeatable read, read only", "start transaction isolation level repeatable read, read write", "begin work isolation level repeatable read, read write", "start work isolation level repeatable read, read only",
285288
"begin not deferrable", "start not deferrable", "begin transaction not deferrable", "start transaction not deferrable", "begin work not deferrable", "start work not deferrable",
286289
"begin read only not deferrable", "start read only not deferrable", "begin transaction read only not deferrable", "start transaction read only not deferrable", "begin work read only not deferrable", "start work read only not deferrable",
287290
"begin read write not deferrable", "start read write not deferrable", "begin transaction read write not deferrable", "start transaction read write not deferrable", "begin work read write not deferrable", "start work read write not deferrable",
@@ -297,7 +300,8 @@
297300
"begin not deferrable isolation level serializable", "start isolation level serializable", "begin transaction not deferrable isolation level serializable", "start transaction isolation level serializable", "begin work not deferrable isolation level serializable", "start work isolation level serializable",
298301
"begin not deferrable isolation level default read write", "start isolation level default read only", "begin transaction not deferrable isolation level default read only", "start transaction isolation level default read write", "begin work not deferrable isolation level default read write", "start work isolation level default read only",
299302
"begin not deferrable isolation level serializable read write", "start isolation level serializable read write", "begin transaction not deferrable isolation level serializable read only", "start transaction isolation level serializable read write", "begin work not deferrable isolation level serializable read write", "start work isolation level serializable read only",
300-
"begin not deferrable isolation level serializable, read write", "start isolation level serializable, read write", "begin transaction not deferrable isolation level serializable, read only", "start transaction isolation level serializable, read write", "begin work not deferrable isolation level serializable, read write", "start work isolation level serializable, read only"
303+
"begin not deferrable isolation level serializable, read write", "start isolation level serializable, read write", "begin transaction not deferrable isolation level serializable, read only", "start transaction isolation level serializable, read write", "begin work not deferrable isolation level serializable, read write", "start work isolation level serializable, read only",
304+
"begin not deferrable isolation level repeatable read, read write", "start isolation level repeatable read, read write", "begin transaction not deferrable isolation level repeatable read, read only", "start transaction isolation level repeatable read, read write", "begin work not deferrable isolation level repeatable read, read write", "start work isolation level repeatable read, read only"
301305
]
302306
},
303307
{
@@ -487,38 +491,38 @@
487491
}
488492
},
489493
{
490-
"name": "SET TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE))] }",
494+
"name": "SET TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE|REPEATABLE READ))] }",
491495
"executorName": "ClientSideStatementSetExecutor",
492496
"resultType": "NO_RESULT",
493497
"statementType": "SET_TRANSACTION_MODE",
494498
"regex": "(?is)\\A\\s*set\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z",
495499
"method": "statementSetPgTransactionMode",
496-
"exampleStatements": ["set transaction read only", "set transaction read write", "set transaction isolation level default", "set transaction isolation level serializable"],
500+
"exampleStatements": ["set transaction read only", "set transaction read write", "set transaction isolation level default", "set transaction isolation level serializable", "set transaction isolation level repeatable read"],
497501
"examplePrerequisiteStatements": ["set autocommit = false"],
498502
"setStatement": {
499503
"propertyName": "TRANSACTION",
500504
"separator": "\\s+",
501-
"allowedValues": "(((?:\\s*|\\s*,\\s*)READ\\s+ONLY|(?:\\s*|\\s*,\\s*)READ\\s+WRITE|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+DEFAULT|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+SERIALIZABLE)+)",
505+
"allowedValues": "(((?:\\s*|\\s*,\\s*)READ\\s+ONLY|(?:\\s*|\\s*,\\s*)READ\\s+WRITE|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+DEFAULT|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+SERIALIZABLE|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+REPEATABLE\\s+READ)+)",
502506
"converterName": "ClientSideStatementValueConverters$PgTransactionModeConverter"
503507
}
504508
},
505509
{
506-
"name": "SET SESSION CHARACTERISTICS AS TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE))] }",
510+
"name": "SET SESSION CHARACTERISTICS AS TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE|REPEATABLE READ))] }",
507511
"executorName": "ClientSideStatementSetExecutor",
508512
"resultType": "NO_RESULT",
509513
"statementType": "SET_READONLY",
510514
"regex": "(?is)\\A\\s*set\\s+session\\s+characteristics\\s+as\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z",
511515
"method": "statementSetPgSessionCharacteristicsTransactionMode",
512-
"exampleStatements": ["set session characteristics as transaction read only", "set session characteristics as transaction read write", "set session characteristics as transaction isolation level default", "set session characteristics as transaction isolation level serializable"],
516+
"exampleStatements": ["set session characteristics as transaction read only", "set session characteristics as transaction read write", "set session characteristics as transaction isolation level default", "set session characteristics as transaction isolation level serializable", "set session characteristics as transaction isolation level repeatable read"],
513517
"setStatement": {
514518
"propertyName": "SESSION\\s+CHARACTERISTICS\\s+AS\\s+TRANSACTION",
515519
"separator": "\\s+",
516-
"allowedValues": "(((?:\\s*|\\s*,\\s*)READ\\s+ONLY|(?:\\s*|\\s*,\\s*)READ\\s+WRITE|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+DEFAULT|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+SERIALIZABLE)+)",
520+
"allowedValues": "(((?:\\s*|\\s*,\\s*)READ\\s+ONLY|(?:\\s*|\\s*,\\s*)READ\\s+WRITE|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+DEFAULT|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+SERIALIZABLE|(?:\\s*|\\s*,\\s*)ISOLATION\\s+LEVEL\\s+REPEATABLE\\s+READ)+)",
517521
"converterName": "ClientSideStatementValueConverters$PgTransactionModeConverter"
518522
}
519523
},
520524
{
521-
"name": "SET DEFAULT_TRANSACTION_ISOLATION =|TO 'SERIALIZABLE'|SERIALIZABLE|DEFAULT",
525+
"name": "SET DEFAULT_TRANSACTION_ISOLATION =|TO 'SERIALIZABLE'|SERIALIZABLE|'REPEATABLE READ'|REPEATABLE READ|DEFAULT",
522526
"executorName": "ClientSideStatementSetExecutor",
523527
"resultType": "NO_RESULT",
524528
"statementType": "SET_READONLY",
@@ -530,13 +534,18 @@
530534
"set default_transaction_isolation to 'serializable'",
531535
"set default_transaction_isolation = 'serializable'",
532536
"set default_transaction_isolation = \"SERIALIZABLE\"",
537+
"set default_transaction_isolation=repeatable read",
538+
"set default_transaction_isolation to repeatable read",
539+
"set default_transaction_isolation to 'repeatable read'",
540+
"set default_transaction_isolation = 'repeatable read'",
541+
"set default_transaction_isolation = \"REPEATABLE READ\"",
533542
"set default_transaction_isolation = DEFAULT",
534543
"set default_transaction_isolation to DEFAULT"
535544
],
536545
"setStatement": {
537546
"propertyName": "default_transaction_isolation",
538547
"separator": "(?:=|\\s+TO\\s+)",
539-
"allowedValues": "(SERIALIZABLE|'SERIALIZABLE'|\"SERIALIZABLE\"|DEFAULT)",
548+
"allowedValues": "(SERIALIZABLE|'SERIALIZABLE'|\"SERIALIZABLE\"|REPEATABLE\\s+READ|'REPEATABLE\\s+READ'|\"REPEATABLE\\s+READ\"|DEFAULT)",
540549
"converterName": "ClientSideStatementValueConverters$PgTransactionIsolationConverter"
541550
}
542551
},

0 commit comments

Comments
 (0)