in src/main/java/com/google/cloud/spanner/pgadapter/statements/BackendConnection.java [300:408]
void doExecute() {
Statement updatedStatement = statement;
try {
checkConnectionState();
// TODO(b/235719478): If the statement is a BEGIN statement and there is a COMMIT statement
// at a later point in the batch, and all the statements in the transaction block are
// SELECT statements, then we should create a read-only transaction. Also, if a transaction
// block always ends with a ROLLBACK, PGAdapter should skip the entire execution of that
// block.
SessionStatement sessionStatement =
getSessionManagementStatement(updatedStatement, parsedStatement);
if (!localStatements.get().isEmpty()
&& localStatements.get().containsKey(statement.getSql())
&& localStatements.get().get(statement.getSql()) != null
&& !Objects.requireNonNull(localStatements.get().get(statement.getSql()))
.hasReplacementStatement()) {
LocalStatement localStatement =
Objects.requireNonNull(localStatements.get().get(statement.getSql()));
result.set(localStatement.execute(BackendConnection.this, statement));
} else if (sessionStatement != null) {
result.set(sessionStatement.execute(sessionState, spannerConnection));
} else if (connectionState == ConnectionState.ABORTED
&& !spannerConnection.isInTransaction()
&& (isRollback(parsedStatement) || isCommit(parsedStatement))) {
result.set(ROLLBACK_RESULT);
} else if (isTransactionStatement(parsedStatement) && sessionState.isForceAutocommit()) {
result.set(NO_RESULT);
} else if (isBegin(parsedStatement) && spannerConnection.isInTransaction()) {
// Ignore the statement as it is a no-op to execute BEGIN when we are already in a
// transaction. TODO: Return a warning.
result.set(NO_RESULT);
} else if ((isCommit(parsedStatement) || isRollback(parsedStatement))
&& !spannerConnection.isInTransaction()) {
// Check if we are in a DDL batch that was created from an explicit transaction.
if (transactionMode == TransactionMode.DDL_BATCH) {
try {
if (isCommit(parsedStatement)) {
spannerConnection.runBatch();
} else {
spannerConnection.abortBatch();
}
} finally {
transactionMode = TransactionMode.IMPLICIT;
}
}
// Ignore the statement as it is a no-op to execute COMMIT/ROLLBACK when we are not in a
// transaction. TODO: Return a warning.
result.set(NO_RESULT);
} else if (SimpleParser.isEmpty(statement.getSql())) {
result.set(NO_RESULT);
} else if (parsedStatement.isDdl()) {
if (analyze) {
result.set(NO_RESULT);
} else {
result.set(ddlExecutor.execute(parsedStatement, statement));
}
} else {
String sqlLowerCase = updatedStatement.getSql().toLowerCase(Locale.ENGLISH);
// Potentially replace pg_catalog table references with common table expressions.
updatedStatement =
parsedStatement.getType() != StatementType.CLIENT_SIDE
&& sessionState.isReplacePgCatalogTables()
? pgCatalog.get().replacePgCatalogTables(updatedStatement, sqlLowerCase)
: updatedStatement;
// TODO: Remove the check for isDelayBeginTransactionStartUntilFirstWrite when that
// feature is able to detect FOR UPDATE clauses as a write.
if (sessionState.isReplaceForUpdateClause()
&& !spannerConnection.isDelayTransactionStartUntilFirstWrite()) {
updatedStatement =
replaceForUpdate(updatedStatement, sqlLowerCase, /* replaceWithHint= */ true);
} else {
updatedStatement =
replaceForUpdate(updatedStatement, sqlLowerCase, /* replaceWithHint= */ false);
}
RemoveEscapeClauseEnum removeEscapeClauseEnum = sessionState.getRemoveEscapeClause();
if (removeEscapeClauseEnum != RemoveEscapeClauseEnum.NONE) {
updatedStatement =
EscapeClauseParser.removeEscapeClauses(
updatedStatement, sqlLowerCase, removeEscapeClauseEnum);
}
updatedStatement = bindStatement(updatedStatement, sqlLowerCase);
result.set(analyzeOrExecute(updatedStatement));
}
} catch (SpannerException spannerException) {
// Executing queries against the information schema in a transaction is unsupported.
// This ensures that those queries are retried using a separate single-use transaction.
if (isUnsupportedConcurrencyModeException(spannerException)) {
try {
result.set(
new QueryResult(
PGAdapterResultSetHelper.toDirectExecuteResultSet(
spannerConnection
.getDatabaseClient()
.singleUse()
.executeQuery(updatedStatement))));
return;
} catch (Exception exception) {
throw setAndReturn(result, exception);
}
}
if (spannerException.getErrorCode() == ErrorCode.CANCELLED || Thread.interrupted()) {
throw setAndReturn(result, PGExceptionFactory.newQueryCancelledException());
} else {
throw setAndReturn(result, spannerException);
}
} catch (Throwable exception) {
throw setAndReturn(result, exception);
}
}