Repository navigation
fix(spanner): show database value for unknown isolation levels #14981
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,10 +42,15 @@ final class DatabaseMetadata { | |
|
|
||
| private final String dialect; | ||
| private final IsolationLevel defaultIsolationLevel; | ||
| private final @Nullable String defaultIsolationLevelValue; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it not be better to keep both these fields? That way, you do not need to reparse the string whenever the actual isolation level enum is needed. It would also make it possible to consider the string value more as a 'view' value, meaning that we could also format it in a more logical way. E.g. PostgreSQL normally returns isolation level names in lower case and without underscores.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, the enum is parsed once and the string is kept as returned by Spanner. |
||
|
|
||
| private DatabaseMetadata(String dialect, IsolationLevel defaultIsolationLevel) { | ||
| private DatabaseMetadata( | ||
| String dialect, | ||
| IsolationLevel defaultIsolationLevel, | ||
| @Nullable String defaultIsolationLevelValue) { | ||
| this.dialect = dialect; | ||
| this.defaultIsolationLevel = defaultIsolationLevel; | ||
| this.defaultIsolationLevelValue = defaultIsolationLevelValue; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -55,20 +60,21 @@ private DatabaseMetadata(String dialect, IsolationLevel defaultIsolationLevel) { | |
| static DatabaseMetadata fromResultSet(ResultSet resultSet) { | ||
| String dialect = Dialect.GOOGLE_STANDARD_SQL.name(); | ||
| IsolationLevel defaultIsolationLevel = IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED; | ||
| String defaultIsolationLevelValue = null; | ||
| while (resultSet.next()) { | ||
| switch (resultSet.getString(0)) { | ||
| case DATABASE_DIALECT_OPTION: | ||
| dialect = resultSet.getString(1); | ||
| break; | ||
| case DEFAULT_TRANSACTION_ISOLATION_OPTION: | ||
| defaultIsolationLevel = | ||
| parseIsolationLevel(resultSet.isNull(1) ? null : resultSet.getString(1)); | ||
| defaultIsolationLevelValue = resultSet.isNull(1) ? null : resultSet.getString(1); | ||
| defaultIsolationLevel = parseIsolationLevel(defaultIsolationLevelValue); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
| return new DatabaseMetadata(dialect, defaultIsolationLevel); | ||
| return new DatabaseMetadata(dialect, defaultIsolationLevel, defaultIsolationLevelValue); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -98,4 +104,8 @@ Dialect getDialect() { | |
| IsolationLevel getDefaultIsolationLevel() { | ||
| return defaultIsolationLevel; | ||
| } | ||
|
|
||
| @Nullable String getDefaultIsolationLevelValue() { | ||
| return defaultIsolationLevelValue; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -654,37 +654,28 @@ public StatementResult statementShowTransactionIsolationLevel() { | |
| : getConnection().getDefaultIsolationLevel(); | ||
| return resultSet( | ||
| "transaction_isolation", | ||
| formatIsolationLevel(getEffectiveIsolationLevel(isolationLevel)), | ||
| getEffectiveIsolationLevel(isolationLevel), | ||
| SHOW_TRANSACTION_ISOLATION_LEVEL); | ||
| } | ||
|
|
||
| @Override | ||
| public StatementResult statementShowDefaultTransactionIsolation() { | ||
| return resultSet( | ||
| "default_transaction_isolation", | ||
| formatIsolationLevel( | ||
| getEffectiveIsolationLevel(getConnection().getDefaultIsolationLevel())), | ||
| getEffectiveIsolationLevel(getConnection().getDefaultIsolationLevel()), | ||
| SHOW_DEFAULT_TRANSACTION_ISOLATION); | ||
| } | ||
|
|
||
| /** Resolves an unspecified isolation level to the database default. */ | ||
| private TransactionOptions.IsolationLevel getEffectiveIsolationLevel( | ||
| TransactionOptions.IsolationLevel isolationLevel) { | ||
| TransactionOptions.IsolationLevel configuredIsolationLevel = | ||
| isolationLevel == TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED | ||
| ? getConnection().getDatabaseClient().getDatabaseDefaultIsolationLevel() | ||
| : isolationLevel; | ||
| switch (configuredIsolationLevel) { | ||
| case ISOLATION_LEVEL_UNSPECIFIED: | ||
| return TransactionOptions.IsolationLevel.SERIALIZABLE; | ||
| case UNRECOGNIZED: | ||
| throw SpannerExceptionFactory.newSpannerException( | ||
| ErrorCode.FAILED_PRECONDITION, | ||
| "The default isolation level of the database is not supported by this version of the" | ||
| + " client."); | ||
| default: | ||
| return configuredIsolationLevel; | ||
| /** Resolves an unspecified isolation level to the database default and formats it. */ | ||
| private String getEffectiveIsolationLevel(TransactionOptions.IsolationLevel isolationLevel) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that you can simplify this method to something like this: private String getEffectiveIsolationLevel(TransactionOptions.IsolationLevel isolationLevel) {
if (isolationLevel != TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED) {
return formatIsolationLevel(isolationLevel);
}
String databaseDefault =
getConnection().getDatabaseClient().getDatabaseDefaultIsolationLevelValue();
if (databaseDefault != null) {
return getConnection().getDialect() == Dialect.POSTGRESQL
? databaseDefault.toLowerCase(Locale.ROOT)
: databaseDefault;
}
return formatIsolationLevel(TransactionOptions.IsolationLevel.SERIALIZABLE);
}(and maybe even further if we keep both the string and enum values in DatabaseMetadata)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simplified as suggested. Spanner already stores these values in lower case with spaces, so the value is returned as-is. |
||
| if (isolationLevel != TransactionOptions.IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED) { | ||
| return formatIsolationLevel(isolationLevel); | ||
| } | ||
| String databaseDefault = | ||
| getConnection().getDatabaseClient().getDatabaseDefaultIsolationLevelValue(); | ||
| return databaseDefault == null | ||
| ? formatIsolationLevel(TransactionOptions.IsolationLevel.SERIALIZABLE) | ||
| : databaseDefault; | ||
| } | ||
|
|
||
| private String formatIsolationLevel(TransactionOptions.IsolationLevel isolationLevel) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.