Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies:
If you are using Gradle 5.x or later, add this to your dependencies:

```Groovy
implementation platform('com.google.cloud:libraries-bom:26.35.0')
implementation platform('com.google.cloud:libraries-bom:26.36.0')

implementation 'com.google.cloud:google-cloud-spanner'
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public String getSql() {
return sql;
}

/** Returns a copy of this statement with the SQL string replaced by the given SQL string. */
public Statement withReplacedSql(String sql) {
return new Statement(sql, this.parameters, this.queryOptions);
}

/** Returns the {@link QueryOptions} that will be used with this {@link Statement}. */
public QueryOptions getQueryOptions() {
return queryOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package com.google.cloud.spanner.connection;

import static com.google.cloud.spanner.connection.StatementHintParser.convertHintsToOptions;

import com.google.api.core.InternalApi;
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.ErrorCode;
import com.google.cloud.spanner.Options.ReadQueryUpdateTransactionOption;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.Statement;
Expand Down Expand Up @@ -169,6 +172,7 @@ public static class ParsedStatement {
private final Statement statement;
private final String sqlWithoutComments;
private final boolean returningClause;
private final ReadQueryUpdateTransactionOption[] optionsFromHints;

private static ParsedStatement clientSideStatement(
ClientSideStatementImpl clientSideStatement,
Expand All @@ -182,15 +186,27 @@ private static ParsedStatement ddl(Statement statement, String sqlWithoutComment
}

private static ParsedStatement query(
Statement statement, String sqlWithoutComments, QueryOptions defaultQueryOptions) {
Statement statement,
String sqlWithoutComments,
QueryOptions defaultQueryOptions,
ReadQueryUpdateTransactionOption[] optionsFromHints) {
return new ParsedStatement(
StatementType.QUERY, null, statement, sqlWithoutComments, defaultQueryOptions, false);
StatementType.QUERY,
null,
statement,
sqlWithoutComments,
defaultQueryOptions,
false,
optionsFromHints);
}

private static ParsedStatement update(
Statement statement, String sqlWithoutComments, boolean returningClause) {
Statement statement,
String sqlWithoutComments,
boolean returningClause,
ReadQueryUpdateTransactionOption[] optionsFromHints) {
return new ParsedStatement(
StatementType.UPDATE, statement, sqlWithoutComments, returningClause);
StatementType.UPDATE, statement, sqlWithoutComments, returningClause, optionsFromHints);
}

private static ParsedStatement unknown(Statement statement, String sqlWithoutComments) {
Expand All @@ -208,18 +224,20 @@ private ParsedStatement(
this.statement = statement;
this.sqlWithoutComments = Preconditions.checkNotNull(sqlWithoutComments);
this.returningClause = false;
this.optionsFromHints = EMPTY_OPTIONS;
}

private ParsedStatement(
StatementType type,
Statement statement,
String sqlWithoutComments,
boolean returningClause) {
this(type, null, statement, sqlWithoutComments, null, returningClause);
boolean returningClause,
ReadQueryUpdateTransactionOption[] optionsFromHints) {
this(type, null, statement, sqlWithoutComments, null, returningClause, optionsFromHints);
}

private ParsedStatement(StatementType type, Statement statement, String sqlWithoutComments) {
this(type, null, statement, sqlWithoutComments, null, false);
this(type, null, statement, sqlWithoutComments, null, false, EMPTY_OPTIONS);
}

private ParsedStatement(
Expand All @@ -228,33 +246,37 @@ private ParsedStatement(
Statement statement,
String sqlWithoutComments,
QueryOptions defaultQueryOptions,
boolean returningClause) {
boolean returningClause,
ReadQueryUpdateTransactionOption[] optionsFromHints) {
Preconditions.checkNotNull(type);
this.type = type;
this.clientSideStatement = clientSideStatement;
this.statement = statement == null ? null : mergeQueryOptions(statement, defaultQueryOptions);
this.sqlWithoutComments = Preconditions.checkNotNull(sqlWithoutComments);
this.returningClause = returningClause;
this.optionsFromHints = optionsFromHints;
}

private ParsedStatement copy(Statement statement, QueryOptions defaultQueryOptions) {
return new ParsedStatement(
this.type,
this.clientSideStatement,
statement,
statement.withReplacedSql(this.statement.getSql()),
this.sqlWithoutComments,
defaultQueryOptions,
this.returningClause);
this.returningClause,
this.optionsFromHints);
}

private ParsedStatement forCache() {
return new ParsedStatement(
this.type,
this.clientSideStatement,
null,
Statement.of(this.statement.getSql()),
this.sqlWithoutComments,
null,
this.returningClause);
this.returningClause,
this.optionsFromHints);
}

@Override
Expand Down Expand Up @@ -287,6 +309,11 @@ public boolean hasReturningClause() {
return this.returningClause;
}

@InternalApi
public ReadQueryUpdateTransactionOption[] getOptionsFromHints() {
return this.optionsFromHints;
}

/**
* @return true if the statement is a query that will return a {@link
* com.google.cloud.spanner.ResultSet}.
Expand Down Expand Up @@ -480,14 +507,23 @@ ParsedStatement parse(Statement statement, QueryOptions defaultQueryOptions) {
}

private ParsedStatement internalParse(Statement statement, QueryOptions defaultQueryOptions) {
StatementHintParser statementHintParser =
new StatementHintParser(getDialect(), statement.getSql());
ReadQueryUpdateTransactionOption[] optionsFromHints = EMPTY_OPTIONS;
if (statementHintParser.hasStatementHints()
&& !statementHintParser.getClientSideStatementHints().isEmpty()) {
statement =
statement.toBuilder().replace(statementHintParser.getSqlWithoutClientSideHints()).build();
optionsFromHints = convertHintsToOptions(statementHintParser.getClientSideStatementHints());
}
String sql = removeCommentsAndTrim(statement.getSql());
ClientSideStatementImpl client = parseClientSideStatement(sql);
if (client != null) {
return ParsedStatement.clientSideStatement(client, statement, sql);
} else if (isQuery(sql)) {
return ParsedStatement.query(statement, sql, defaultQueryOptions);
return ParsedStatement.query(statement, sql, defaultQueryOptions, optionsFromHints);
} else if (isUpdateStatement(sql)) {
return ParsedStatement.update(statement, sql, checkReturningClause(sql));
return ParsedStatement.update(statement, sql, checkReturningClause(sql), optionsFromHints);
} else if (isDdlStatement(sql)) {
return ParsedStatement.ddl(statement, sql);
}
Expand Down Expand Up @@ -621,6 +657,10 @@ public String removeCommentsAndTrim(String sql) {
/** Removes any statement hints at the beginning of the statement. */
abstract String removeStatementHint(String sql);

@VisibleForTesting
static final ReadQueryUpdateTransactionOption[] EMPTY_OPTIONS =
new ReadQueryUpdateTransactionOption[0];

/** Parameter information with positional parameters translated to named parameters. */
@InternalApi
public static class ParametersInfo {
Expand Down Expand Up @@ -697,9 +737,10 @@ public boolean checkReturningClause(String sql) {
return checkReturningClauseInternal(sql);
}

abstract Dialect getDialect();

/**
* <<<<<<< HEAD Returns true if this dialect supports nested comments. ======= <<<<<<< HEAD
* Returns true if this dialect supports nested comments. >>>>>>> main
* Returns true if this dialect supports nested comments.
*
* <ul>
* <li>This method should return false for dialects that consider this to be a valid comment:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.Options;
import com.google.cloud.spanner.Options.QueryOption;
import com.google.cloud.spanner.Options.ReadQueryUpdateTransactionOption;
import com.google.cloud.spanner.Options.RpcPriority;
import com.google.cloud.spanner.Options.UpdateOption;
import com.google.cloud.spanner.PartitionOptions;
Expand Down Expand Up @@ -1154,14 +1155,16 @@ public ResultSet partitionQuery(
"Only queries can be partitioned. Invalid statement: " + query.getSql());
}

QueryOption[] combinedOptions = concat(parsedStatement.getOptionsFromHints(), options);
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork();
return get(
transaction.partitionQueryAsync(
CallType.SYNC,
parsedStatement,
getEffectivePartitionOptions(partitionOptions),
mergeDataBoost(
mergeQueryRequestOptions(parsedStatement, mergeQueryStatementTag(options)))));
mergeQueryRequestOptions(
parsedStatement, mergeQueryStatementTag(combinedOptions)))));
}

private PartitionOptions getEffectivePartitionOptions(
Expand Down Expand Up @@ -1455,6 +1458,34 @@ private List<ParsedStatement> parseUpdateStatements(Iterable<Statement> updates)
return parsedStatements;
}

private UpdateOption[] concat(
ReadQueryUpdateTransactionOption[] statementOptions, UpdateOption[] argumentOptions) {
if (statementOptions == null || statementOptions.length == 0) {
return argumentOptions;
}
if (argumentOptions == null || argumentOptions.length == 0) {
return statementOptions;
}
UpdateOption[] result =
Arrays.copyOf(statementOptions, statementOptions.length + argumentOptions.length);
System.arraycopy(argumentOptions, 0, result, statementOptions.length, argumentOptions.length);
return result;
}

private QueryOption[] concat(
ReadQueryUpdateTransactionOption[] statementOptions, QueryOption[] argumentOptions) {
if (statementOptions == null || statementOptions.length == 0) {
return argumentOptions;
}
if (argumentOptions == null || argumentOptions.length == 0) {
return statementOptions;
}
QueryOption[] result =
Arrays.copyOf(statementOptions, statementOptions.length + argumentOptions.length);
System.arraycopy(argumentOptions, 0, result, statementOptions.length, argumentOptions.length);
return result;
}

private QueryOption[] mergeDataBoost(QueryOption... options) {
if (this.dataBoostEnabled) {
options = appendQueryOption(options, Options.dataBoostEnabled(true));
Expand Down Expand Up @@ -1531,19 +1562,20 @@ private ResultSet internalExecuteQuery(
&& (analyzeMode != AnalyzeMode.NONE || statement.hasReturningClause())),
"Statement must either be a query or a DML mode with analyzeMode!=NONE or returning clause");
boolean isInternalMetadataQuery = isInternalMetadataQuery(options);
QueryOption[] combinedOptions = concat(statement.getOptionsFromHints(), options);
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork(isInternalMetadataQuery);
if (autoPartitionMode
&& statement.getType() == StatementType.QUERY
&& !isInternalMetadataQuery) {
return runPartitionedQuery(
statement.getStatement(), PartitionOptions.getDefaultInstance(), options);
statement.getStatement(), PartitionOptions.getDefaultInstance(), combinedOptions);
}
return get(
transaction.executeQueryAsync(
callType,
statement,
analyzeMode,
mergeQueryRequestOptions(statement, mergeQueryStatementTag(options))));
mergeQueryRequestOptions(statement, mergeQueryStatementTag(combinedOptions))));
}

private AsyncResultSet internalExecuteQueryAsync(
Expand All @@ -1558,25 +1590,27 @@ private AsyncResultSet internalExecuteQueryAsync(
ConnectionPreconditions.checkState(
!(autoPartitionMode && statement.getType() == StatementType.QUERY),
"Partitioned queries cannot be executed asynchronously");
UnitOfWork transaction =
getCurrentUnitOfWorkOrStartNewUnitOfWork(isInternalMetadataQuery(options));
boolean isInternalMetadataQuery = isInternalMetadataQuery(options);
QueryOption[] combinedOptions = concat(statement.getOptionsFromHints(), options);
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork(isInternalMetadataQuery);
return ResultSets.toAsyncResultSet(
transaction.executeQueryAsync(
callType,
statement,
analyzeMode,
mergeQueryRequestOptions(statement, mergeQueryStatementTag(options))),
mergeQueryRequestOptions(statement, mergeQueryStatementTag(combinedOptions))),
spanner.getAsyncExecutorProvider(),
options);
combinedOptions);
}

private ApiFuture<Long> internalExecuteUpdateAsync(
final CallType callType, final ParsedStatement update, UpdateOption... options) {
Preconditions.checkArgument(
update.getType() == StatementType.UPDATE, "Statement must be an update");
UpdateOption[] combinedOptions = concat(update.getOptionsFromHints(), options);
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork();
return transaction.executeUpdateAsync(
callType, update, mergeUpdateRequestOptions(mergeUpdateStatementTag(options)));
callType, update, mergeUpdateRequestOptions(mergeUpdateStatementTag(combinedOptions)));
}

private ApiFuture<ResultSet> internalAnalyzeUpdateAsync(
Expand All @@ -1586,16 +1620,22 @@ private ApiFuture<ResultSet> internalAnalyzeUpdateAsync(
UpdateOption... options) {
Preconditions.checkArgument(
update.getType() == StatementType.UPDATE, "Statement must be an update");
UpdateOption[] combinedOptions = concat(update.getOptionsFromHints(), options);
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork();
return transaction.analyzeUpdateAsync(
callType, update, analyzeMode, mergeUpdateRequestOptions(mergeUpdateStatementTag(options)));
callType,
update,
analyzeMode,
mergeUpdateRequestOptions(mergeUpdateStatementTag(combinedOptions)));
}

private ApiFuture<long[]> internalExecuteBatchUpdateAsync(
CallType callType, List<ParsedStatement> updates, UpdateOption... options) {
UpdateOption[] combinedOptions =
updates.isEmpty() ? options : concat(updates.get(0).getOptionsFromHints(), options);
UnitOfWork transaction = getCurrentUnitOfWorkOrStartNewUnitOfWork();
return transaction.executeBatchUpdateAsync(
callType, updates, mergeUpdateRequestOptions(mergeUpdateStatementTag(options)));
callType, updates, mergeUpdateRequestOptions(mergeUpdateStatementTag(combinedOptions)));
}

private UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public class PostgreSQLStatementParser extends AbstractStatementParser {
ClientSideStatements.getInstance(Dialect.POSTGRESQL).getCompiledStatements()));
}

@Override
Dialect getDialect() {
return Dialect.POSTGRESQL;
}

/**
* Indicates whether the parser supports the {@code EXPLAIN} clause. The PostgreSQL parser does
* not support it.
Expand Down
Loading