7070import com .google .cloud .spanner .pgadapter .wireoutput .ReadyResponse ;
7171import com .google .cloud .spanner .pgadapter .wireoutput .ReadyResponse .Status ;
7272import com .google .common .annotations .VisibleForTesting ;
73+ import com .google .common .base .MoreObjects ;
7374import com .google .common .base .Preconditions ;
7475import com .google .common .base .Stopwatch ;
7576import com .google .common .base .Suppliers ;
8283import com .google .common .util .concurrent .ListeningExecutorService ;
8384import com .google .common .util .concurrent .MoreExecutors ;
8485import com .google .common .util .concurrent .SettableFuture ;
86+ import com .google .spanner .v1 .TransactionOptions .IsolationLevel ;
8587import io .opentelemetry .api .common .AttributeKey ;
8688import io .opentelemetry .api .common .Attributes ;
8789import io .opentelemetry .api .trace .Span ;
@@ -535,6 +537,9 @@ private StatementResult executeOnSpannerWithLogging(Statement statement) {
535537 () -> String .format ("Statement: %s" , statement .getSql ())));
536538 Stopwatch stopwatch = Stopwatch .createStarted ();
537539 StatementResult result = spannerConnection .execute (statement );
540+ if (parsedStatement .getType () == StatementType .CLIENT_SIDE ) {
541+ syncSessionStateWithClientSideStatement ();
542+ }
538543 Duration executionDuration = stopwatch .elapsed ();
539544 metrics .recordClientLibLatency (executionDuration .toMillis (), metricAttributes );
540545 logger .log (
@@ -564,6 +569,52 @@ private StatementResult executeOnSpannerWithLogging(Statement statement) {
564569 }
565570 }
566571
572+ private void syncSessionStateWithClientSideStatement () {
573+ ClientSideStatementType type = parsedStatement .getClientSideStatementType ();
574+ if (type == ClientSideStatementType .SET_DEFAULT_TRANSACTION_ISOLATION
575+ || (type == ClientSideStatementType .SET_READONLY
576+ && statement .getSql ().toLowerCase (Locale .ENGLISH ).contains ("isolation" ))) {
577+ if (spannerConnection .getDefaultIsolationLevel () != null ) {
578+ sessionState .setDefaultIsolationLevel (
579+ spannerConnection .getDefaultIsolationLevel (), spannerConnection .isInTransaction ());
580+ syncConnectionDefaultIsolationLevel (sessionState , spannerConnection );
581+ }
582+ } else if (type == ClientSideStatementType .BEGIN
583+ || (type == ClientSideStatementType .SET_TRANSACTION_MODE
584+ && statement .getSql ().toLowerCase (Locale .ENGLISH ).contains ("isolation" ))) {
585+ if (spannerConnection .isInTransaction ()) {
586+ syncTransactionIsolationLevel ();
587+ }
588+ }
589+ }
590+
591+ private void syncTransactionIsolationLevel () {
592+ String isolationLevel = getTransactionIsolationLevelFromConnection ();
593+ if (!Objects .equals (
594+ isolationLevel , sessionState .get (null , "transaction_isolation" ).getSetting ())) {
595+ sessionState .setLocal (null , "transaction_isolation" , isolationLevel );
596+ }
597+ }
598+
599+ private String getTransactionIsolationLevelFromConnection () {
600+ try {
601+ StatementResult showResult =
602+ spannerConnection .execute (Statement .of ("show transaction isolation level" ));
603+ if (showResult != null && showResult .getResultSet () != null ) {
604+ try (ResultSet rs = showResult .getResultSet ()) {
605+ if (rs .next ()) {
606+ String level = rs .getString (0 );
607+ if (level != null && !"ISOLATION_LEVEL_UNSPECIFIED" .equalsIgnoreCase (level )) {
608+ return SessionState .toPgIsolationLevel (level );
609+ }
610+ }
611+ }
612+ }
613+ } catch (Throwable ignore ) {
614+ }
615+ return SessionState .toPgIsolationLevel (sessionState .getDefaultIsolationLevel ());
616+ }
617+
567618 /**
568619 * Returns true if the given exception is the error that is returned by Cloud Spanner when an
569620 * INFORMATION_SCHEMA query is not executed in a single-use read-only transaction.
@@ -922,6 +973,7 @@ void doExecute() {
922973 this .connectionId = connectionId ;
923974 this .closeAllPortals = closeAllPortals ;
924975 this .sessionState = new SessionState (optionsMetadata );
976+ this .sessionState .setInitialIsolationLevel (spannerConnection .getDefaultIsolationLevel ());
925977 this .pgCatalog =
926978 Suppliers .memoize (
927979 () -> new PgCatalog (BackendConnection .this .sessionState , wellKnownClient .get ()));
@@ -1138,6 +1190,8 @@ public void initSessionSetting(String name, String value) {
11381190 ParsedStatement parsedStatement = statementParser .parse (statement );
11391191 if (parsedStatement .getType () == StatementType .CLIENT_SIDE ) {
11401192 this .spannerConnection .execute (Statement .of (statement .getSql ()));
1193+ this .sessionState .setInitialIsolationLevel (
1194+ this .spannerConnection .getDefaultIsolationLevel ());
11411195 continue ;
11421196 }
11431197 } catch (Throwable ignore ) {
@@ -1166,7 +1220,16 @@ public void initSessionSetting(String name, String value, boolean overwrite) {
11661220 return ;
11671221 }
11681222 if (overwrite || this .sessionState .tryGet (key .schema , key .name ) == null ) {
1169- this .sessionState .setConnectionStartupValue (key .schema , key .name , value );
1223+ if (key .schema == null && "default_transaction_isolation" .equalsIgnoreCase (key .name )) {
1224+ try {
1225+ IsolationLevel level = SessionState .toSpannerIsolationLevel (value );
1226+ this .spannerConnection .setDefaultIsolationLevel (level );
1227+ this .sessionState .setInitialIsolationLevel (level );
1228+ } catch (Throwable ignore ) {
1229+ }
1230+ } else {
1231+ this .sessionState .setConnectionStartupValue (key .schema , key .name , value );
1232+ }
11701233 }
11711234 }
11721235
@@ -1234,6 +1297,9 @@ private void flush(boolean isSync) {
12341297 if (canUseBatch ) {
12351298 index += executeStatementsInBatch (index );
12361299 } else {
1300+ if (isBegin (index ) && !spannerConnection .isInTransaction ()) {
1301+ sessionState .commit ();
1302+ }
12371303 bufferedStatement .execute ();
12381304
12391305 if (isBegin (index )) {
@@ -1246,6 +1312,7 @@ private void flush(boolean isSync) {
12461312 } else {
12471313 sessionState .rollback ();
12481314 }
1315+ syncConnectionDefaultIsolationLevel (sessionState , spannerConnection );
12491316 closeAllPortals .run ();
12501317 clearCurrentTransaction ();
12511318 }
@@ -1269,6 +1336,7 @@ private void flush(boolean isSync) {
12691336 } else if (spannerConnection .isDdlBatchActive ()) {
12701337 spannerConnection .abortBatch ();
12711338 }
1339+ syncConnectionDefaultIsolationLevel (sessionState , spannerConnection );
12721340 } finally {
12731341 failBufferedStatements (PGExceptionFactory .newQueryCancelledException ());
12741342 bufferedStatements .clear ();
@@ -1388,13 +1456,31 @@ private void endImplicitTransaction() {
13881456 spannerConnection .setStatementTag (null );
13891457 spannerConnection .commit ();
13901458 }
1459+ syncConnectionDefaultIsolationLevel (sessionState , spannerConnection );
13911460 }
13921461 } finally {
13931462 connectionState = ConnectionState .IDLE ;
13941463 currentTransactionId = null ;
13951464 }
13961465 }
13971466
1467+ static void syncConnectionDefaultIsolationLevel (
1468+ SessionState sessionState , Connection spannerConnection ) {
1469+ if (!spannerConnection .isInTransaction ()
1470+ && !spannerConnection .isTransactionStarted ()
1471+ && !spannerConnection .isDdlBatchActive ()
1472+ && !spannerConnection .isDmlBatchActive ()) {
1473+ IsolationLevel target = sessionState .getDefaultIsolationLevel ();
1474+ IsolationLevel current =
1475+ MoreObjects .firstNonNull (
1476+ spannerConnection .getDefaultIsolationLevel (),
1477+ IsolationLevel .ISOLATION_LEVEL_UNSPECIFIED );
1478+ if (current != target ) {
1479+ spannerConnection .setDefaultIsolationLevel (target );
1480+ }
1481+ }
1482+ }
1483+
13981484 /**
13991485 * Prepares the connection for executing a DDL statement. This can include committing the current
14001486 * transaction.
@@ -1428,6 +1514,7 @@ private void prepareExecuteDdl(BufferedStatement<?> bufferedStatement) {
14281514 if (spannerConnection .isInTransaction ()) {
14291515 spannerConnection .commit ();
14301516 sessionState .commit ();
1517+ syncConnectionDefaultIsolationLevel (sessionState , spannerConnection );
14311518 if (transactionMode == TransactionMode .EXPLICIT ) {
14321519 // Convert the explicit transaction to a DDL batch.
14331520 transactionMode = TransactionMode .DDL_BATCH ;
@@ -1469,6 +1556,7 @@ private void prepareExecuteDdl(BufferedStatement<?> bufferedStatement) {
14691556 if (spannerConnection .isInTransaction ()) {
14701557 spannerConnection .commit ();
14711558 sessionState .commit ();
1559+ syncConnectionDefaultIsolationLevel (sessionState , spannerConnection );
14721560 if (transactionMode == TransactionMode .EXPLICIT ) {
14731561 // Convert the explicit transaction to a DDL batch.
14741562 transactionMode = TransactionMode .DDL_BATCH ;
@@ -1573,8 +1661,7 @@ SessionStatement getSessionManagementStatement(
15731661 Statement statement , ParsedStatement parsedStatement ) {
15741662 if (parsedStatement .getType () == StatementType .UNKNOWN
15751663 || (parsedStatement .getType () == StatementType .QUERY && isCommand (SHOW , statement .getSql ()))
1576- || (parsedStatement .getType () == StatementType .CLIENT_SIDE
1577- && parsedStatement .getClientSideStatementType () == ClientSideStatementType .RESET_ALL )) {
1664+ || parsedStatement .getType () == StatementType .CLIENT_SIDE ) {
15781665 return SessionStatementParser .parse (parsedStatement , statement .getSql ());
15791666 }
15801667 return null ;
0 commit comments