Skip to content

feat: support PostgreSQL isolation level statements #3706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 28, 2025
Merged
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner</artifactId>
<version>6.84.0</version>
<version>6.89.0</version>
</dependency>

```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,11 @@ public PgTransactionMode convert(String value) {
} else if (valueWithSingleSpaces.substring(currentIndex).startsWith("read write")) {
currentIndex += "read write".length();
mode.setAccessMode(AccessMode.READ_WRITE_TRANSACTION);
} else if (valueWithSingleSpaces
.substring(currentIndex)
.startsWith("isolation level repeatable read")) {
currentIndex += "isolation level repeatable read".length();
mode.setIsolationLevel(IsolationLevel.ISOLATION_LEVEL_REPEATABLE_READ);
} else if (valueWithSingleSpaces
.substring(currentIndex)
.startsWith("isolation level serializable")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,14 @@ public StatementResult statementBeginTransaction(

@Override
public StatementResult statementBeginPgTransaction(@Nullable PgTransactionMode transactionMode) {
getConnection().beginTransaction();
if (transactionMode == null
|| transactionMode.getIsolationLevel() == null
|| transactionMode.getIsolationLevel() == IsolationLevel.ISOLATION_LEVEL_DEFAULT) {
getConnection().beginTransaction();
} else {
getConnection()
.beginTransaction(transactionMode.getIsolationLevel().getSpannerIsolationLevel());
}
if (transactionMode != null) {
statementSetPgTransactionMode(transactionMode);
}
Expand Down Expand Up @@ -501,6 +508,10 @@ public StatementResult statementSetPgTransactionMode(PgTransactionMode transacti
@Override
public StatementResult statementSetPgSessionCharacteristicsTransactionMode(
PgTransactionMode transactionMode) {
if (transactionMode.getIsolationLevel() != null) {
getConnection()
.setDefaultIsolationLevel(transactionMode.getIsolationLevel().getSpannerIsolationLevel());
}
if (transactionMode.getAccessMode() != null) {
switch (transactionMode.getAccessMode()) {
case READ_ONLY_TRANSACTION:
Expand All @@ -518,7 +529,11 @@ public StatementResult statementSetPgSessionCharacteristicsTransactionMode(

@Override
public StatementResult statementSetPgDefaultTransactionIsolation(IsolationLevel isolationLevel) {
// no-op
getConnection()
.setDefaultIsolationLevel(
isolationLevel == null
? TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED
: isolationLevel.getSpannerIsolationLevel());
return noResult(SET_DEFAULT_TRANSACTION_ISOLATION);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.google.cloud.spanner.connection;

import com.google.spanner.v1.TransactionOptions;
import com.google.spanner.v1.TransactionOptions.IsolationLevel;
import java.util.Objects;

/**
Expand All @@ -40,15 +42,30 @@ public String toString() {
}

enum IsolationLevel {
ISOLATION_LEVEL_DEFAULT("ISOLATION LEVEL DEFAULT", "DEFAULT"),
ISOLATION_LEVEL_SERIALIZABLE("ISOLATION LEVEL SERIALIZABLE", "SERIALIZABLE");
ISOLATION_LEVEL_DEFAULT(
"ISOLATION LEVEL DEFAULT",
"DEFAULT",
TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED),
ISOLATION_LEVEL_SERIALIZABLE(
"ISOLATION LEVEL SERIALIZABLE",
"SERIALIZABLE",
TransactionOptions.IsolationLevel.SERIALIZABLE),
ISOLATION_LEVEL_REPEATABLE_READ(
"ISOLATION LEVEL REPEATABLE READ",
"REPEATABLE READ",
TransactionOptions.IsolationLevel.REPEATABLE_READ);

private final String statementString;
private final String shortStatementString;
private final TransactionOptions.IsolationLevel spannerIsolationLevel;

IsolationLevel(String statement, String shortStatementString) {
IsolationLevel(
String statement,
String shortStatementString,
TransactionOptions.IsolationLevel spannerIsolationLevel) {
this.statementString = statement;
this.shortStatementString = shortStatementString;
this.spannerIsolationLevel = spannerIsolationLevel;
}

/**
Expand All @@ -67,6 +84,10 @@ public String getShortStatementString() {
return shortStatementString;
}

public TransactionOptions.IsolationLevel getSpannerIsolationLevel() {
return spannerIsolationLevel;
}

@Override
public String toString() {
return statementString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,21 +267,24 @@
"exampleStatements": []
},
{
"name": "{START | BEGIN} [TRANSACTION | WORK] [{ (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE))] [[,] NOT DEFERRABLE]}]",
"name": "{START | BEGIN} [TRANSACTION | WORK] [{ (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE|REPEATABLE READ))] [[,] NOT DEFERRABLE]}]",
"executorName": "ClientSideStatementPgBeginExecutor",
"resultType": "NO_RESULT",
"statementType": "BEGIN",
"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",
"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",
"method": "statementBeginPgTransaction",
"exampleStatements": [
"begin", "start", "begin transaction", "start transaction", "begin work", "start work",
"begin read only", "start read only", "begin transaction read only", "start transaction read only", "begin work read only", "start work read only",
"begin read write", "start read write", "begin transaction read write", "start transaction read write", "begin work read write", "start work read write",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"begin not deferrable", "start not deferrable", "begin transaction not deferrable", "start transaction not deferrable", "begin work not deferrable", "start work not deferrable",
"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",
"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",
Expand All @@ -297,7 +300,8 @@
"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",
"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",
"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",
"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"
"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",
"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"
]
},
{
Expand Down Expand Up @@ -487,38 +491,38 @@
}
},
{
"name": "SET TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE))] }",
"name": "SET TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE|REPEATABLE READ))] }",
"executorName": "ClientSideStatementSetExecutor",
"resultType": "NO_RESULT",
"statementType": "SET_TRANSACTION_MODE",
"regex": "(?is)\\A\\s*set\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z",
"method": "statementSetPgTransactionMode",
"exampleStatements": ["set transaction read only", "set transaction read write", "set transaction isolation level default", "set transaction isolation level serializable"],
"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"],
"examplePrerequisiteStatements": ["set autocommit = false"],
"setStatement": {
"propertyName": "TRANSACTION",
"separator": "\\s+",
"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)+)",
"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)+)",
"converterName": "ClientSideStatementValueConverters$PgTransactionModeConverter"
}
},
{
"name": "SET SESSION CHARACTERISTICS AS TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE))] }",
"name": "SET SESSION CHARACTERISTICS AS TRANSACTION { (READ ONLY|READ WRITE) [[,] (ISOLATION LEVEL (DEFAULT|SERIALIZABLE|REPEATABLE READ))] }",
"executorName": "ClientSideStatementSetExecutor",
"resultType": "NO_RESULT",
"statementType": "SET_READONLY",
"regex": "(?is)\\A\\s*set\\s+session\\s+characteristics\\s+as\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z",
"method": "statementSetPgSessionCharacteristicsTransactionMode",
"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"],
"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"],
"setStatement": {
"propertyName": "SESSION\\s+CHARACTERISTICS\\s+AS\\s+TRANSACTION",
"separator": "\\s+",
"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)+)",
"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)+)",
"converterName": "ClientSideStatementValueConverters$PgTransactionModeConverter"
}
},
{
"name": "SET DEFAULT_TRANSACTION_ISOLATION =|TO 'SERIALIZABLE'|SERIALIZABLE|DEFAULT",
"name": "SET DEFAULT_TRANSACTION_ISOLATION =|TO 'SERIALIZABLE'|SERIALIZABLE|'REPEATABLE READ'|REPEATABLE READ|DEFAULT",
"executorName": "ClientSideStatementSetExecutor",
"resultType": "NO_RESULT",
"statementType": "SET_READONLY",
Expand All @@ -530,13 +534,18 @@
"set default_transaction_isolation to 'serializable'",
"set default_transaction_isolation = 'serializable'",
"set default_transaction_isolation = \"SERIALIZABLE\"",
"set default_transaction_isolation=repeatable read",
"set default_transaction_isolation to repeatable read",
"set default_transaction_isolation to 'repeatable read'",
"set default_transaction_isolation = 'repeatable read'",
"set default_transaction_isolation = \"REPEATABLE READ\"",
"set default_transaction_isolation = DEFAULT",
"set default_transaction_isolation to DEFAULT"
],
"setStatement": {
"propertyName": "default_transaction_isolation",
"separator": "(?:=|\\s+TO\\s+)",
"allowedValues": "(SERIALIZABLE|'SERIALIZABLE'|\"SERIALIZABLE\"|DEFAULT)",
"allowedValues": "(SERIALIZABLE|'SERIALIZABLE'|\"SERIALIZABLE\"|REPEATABLE\\s+READ|'REPEATABLE\\s+READ'|\"REPEATABLE\\s+READ\"|DEFAULT)",
"converterName": "ClientSideStatementValueConverters$PgTransactionIsolationConverter"
}
},
Expand Down
Loading
Loading