diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractBaseUnitOfWork.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractBaseUnitOfWork.java index f04026429f5..5f4facf1489 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractBaseUnitOfWork.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractBaseUnitOfWork.java @@ -317,7 +317,7 @@ ResponseT getWithStatementTimeout( } catch (TimeoutException e) { throw SpannerExceptionFactory.newSpannerException( ErrorCode.DEADLINE_EXCEEDED, - "Statement execution timeout occurred for " + statement.getSqlWithoutComments(), + "Statement execution timeout occurred for " + statement.getSql(), e); } catch (ExecutionException e) { Throwable cause = e.getCause(); @@ -331,7 +331,7 @@ ResponseT getWithStatementTimeout( } throw SpannerExceptionFactory.newSpannerException( ErrorCode.fromGrpcStatus(Status.fromThrowable(e)), - "Statement execution failed for " + statement.getSqlWithoutComments(), + "Statement execution failed for " + statement.getSql(), e); } catch (InterruptedException e) { throw SpannerExceptionFactory.newSpannerException( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java index 7b618a65b29..652bc9b5e7b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java @@ -425,7 +425,17 @@ Statement mergeQueryOptions(Statement statement, QueryOptions defaultQueryOption .build(); } - /** @return the SQL statement with all comments removed from the SQL string. */ + /** @return the original SQL statement */ + @InternalApi + public String getSql() { + return statement.getSql(); + } + + /** + * @return the SQL statement with all comments removed from the SQL string. + * @deprecated use {@link #getSql()} instead + */ + @Deprecated @InternalApi public String getSqlWithoutComments() { return sqlWithoutComments.get(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementBeginExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementBeginExecutor.java index d1b01526724..7f854c0ccab 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementBeginExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementBeginExecutor.java @@ -46,8 +46,7 @@ class ClientSideStatementBeginExecutor implements ClientSideStatementExecutor { @Override public StatementResult execute(ConnectionStatementExecutor connection, ParsedStatement statement) throws Exception { - return (StatementResult) - method.invoke(connection, getParameterValue(statement.getSqlWithoutComments())); + return (StatementResult) method.invoke(connection, getParameterValue(statement.getSql())); } IsolationLevel getParameterValue(String sql) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExplainExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExplainExecutor.java index 767d6917be6..43b84f48123 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExplainExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementExplainExecutor.java @@ -50,8 +50,7 @@ class ClientSideStatementExplainExecutor implements ClientSideStatementExecutor @Override public StatementResult execute(ConnectionStatementExecutor connection, ParsedStatement statement) throws Exception { - return (StatementResult) - method.invoke(connection, getParameterValue(statement.getSqlWithoutComments())); + return (StatementResult) method.invoke(connection, getParameterValue(statement.getSql())); } String getParameterValue(String sql) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPartitionExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPartitionExecutor.java index 0307ff517bb..c96ee155341 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPartitionExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPartitionExecutor.java @@ -49,7 +49,7 @@ public StatementResult execute( } String getParameterValue(ParsedStatement parsedStatement) { - Matcher matcher = statement.getPattern().matcher(parsedStatement.getSqlWithoutComments()); + Matcher matcher = statement.getPattern().matcher(parsedStatement.getSql()); if (matcher.find() && matcher.groupCount() >= 2) { String space = matcher.group(1); String value = matcher.group(2); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPgBeginExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPgBeginExecutor.java index c1d00d81b55..fae41de18c1 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPgBeginExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementPgBeginExecutor.java @@ -45,8 +45,7 @@ class ClientSideStatementPgBeginExecutor implements ClientSideStatementExecutor @Override public StatementResult execute(ConnectionStatementExecutor connection, ParsedStatement statement) throws Exception { - return (StatementResult) - method.invoke(connection, getParameterValue(statement.getSqlWithoutComments())); + return (StatementResult) method.invoke(connection, getParameterValue(statement.getSql())); } PgTransactionMode getParameterValue(String sql) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionExecutor.java index 1534f04b3a4..7e3c30d9f70 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionExecutor.java @@ -65,7 +65,7 @@ String getParameterValue(ParsedStatement parsedStatement) { // 2. If the matcher matches and returns zero groups, we know that the statement is valid, but // that it does not contain a partition-id in the SQL statement. The partition-id must then // be included in the statement as a query parameter. - Matcher matcher = statement.getPattern().matcher(parsedStatement.getSqlWithoutComments()); + Matcher matcher = statement.getPattern().matcher(parsedStatement.getSql()); if (matcher.find() && matcher.groupCount() >= 1) { String value = matcher.group(1); if (!Strings.isNullOrEmpty(value)) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionedQueryExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionedQueryExecutor.java index ba42db1f9d3..c95f2203fc8 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionedQueryExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementRunPartitionedQueryExecutor.java @@ -50,7 +50,7 @@ public StatementResult execute( } String getParameterValue(ParsedStatement parsedStatement) { - Matcher matcher = statement.getPattern().matcher(parsedStatement.getSqlWithoutComments()); + Matcher matcher = statement.getPattern().matcher(parsedStatement.getSql()); if (matcher.find() && matcher.groupCount() >= 2) { // Include the spacing group in case the query is enclosed in parentheses like this: // `run partitioned query(select * from foo)` diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java index 38c7c364106..5bb0a4c8d3b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementSetExecutor.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.connection; import com.google.cloud.Tuple; +import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStatement; @@ -27,6 +28,7 @@ import com.google.common.util.concurrent.UncheckedExecutionException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.nio.CharBuffer; import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -104,8 +106,8 @@ public StatementResult execute(ConnectionStatementExecutor connection, ParsedSta try { value = this.cache.get( - statement.getSqlWithoutComments(), - () -> getParameterValue(statement.getSqlWithoutComments())); + statement.getSql(), + () -> getParameterValue(connection.getDialect(), statement.getSql())); } catch (ExecutionException | UncheckedExecutionException executionException) { throw SpannerExceptionFactory.asSpannerException(executionException.getCause()); } @@ -115,8 +117,13 @@ public StatementResult execute(ConnectionStatementExecutor connection, ParsedSta return (StatementResult) method.invoke(connection, value.x()); } - Tuple getParameterValue(String sql) { - Matcher matcher = allowedValuesPattern.matcher(sql); + Tuple getParameterValue(Dialect dialect, String sql) { + // Get rid of any spaces/comments at the start of the string. + SimpleParser simpleParser = new SimpleParser(dialect, sql); + simpleParser.skipWhitespaces(); + // Create a wrapper around the SQL string from the point after the first whitespace. + CharBuffer sqlAfterWhitespaces = CharBuffer.wrap(sql, simpleParser.getPos(), sql.length()); + Matcher matcher = allowedValuesPattern.matcher(sqlAfterWhitespaces); if (matcher.find() && matcher.groupCount() >= 2) { boolean local = matcher.group(1) != null && "local".equalsIgnoreCase(matcher.group(1).trim()); String value = matcher.group(2); @@ -130,7 +137,7 @@ Tuple getParameterValue(String sql) { "Unknown value for %s: %s", this.statement.getSetStatement().getPropertyName(), value)); } else { - Matcher invalidMatcher = this.statement.getPattern().matcher(sql); + Matcher invalidMatcher = this.statement.getPattern().matcher(sqlAfterWhitespaces); int valueGroup = this.supportsLocal ? 2 : 1; if (invalidMatcher.find() && invalidMatcher.groupCount() == valueGroup) { String invalidValue = invalidMatcher.group(valueGroup); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index f0e8ad6166b..c1e8839534f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -382,6 +382,7 @@ public Spanner getSpanner() { private DdlClient createDdlClient() { return DdlClient.newBuilder() .setDatabaseAdminClient(spanner.getDatabaseAdminClient()) + .setDialectSupplier(this::getDialect) .setProjectId(options.getProjectId()) .setInstanceId(options.getInstanceId()) .setDatabaseName(options.getDatabaseName()) @@ -1424,8 +1425,7 @@ private StatementResult internalExecute( default: } throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INVALID_ARGUMENT, - "Unknown statement: " + parsedStatement.getSqlWithoutComments()); + ErrorCode.INVALID_ARGUMENT, "Unknown statement: " + parsedStatement.getSql()); } @VisibleForTesting @@ -1470,8 +1470,7 @@ private static ResultType getResultType(ParsedStatement parsedStatement) { case UNKNOWN: default: throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INVALID_ARGUMENT, - "Unknown statement: " + parsedStatement.getSqlWithoutComments()); + ErrorCode.INVALID_ARGUMENT, "Unknown statement: " + parsedStatement.getSql()); } } @@ -1503,8 +1502,7 @@ public AsyncStatementResult executeAsync(Statement statement) { default: } throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INVALID_ARGUMENT, - "Unknown statement: " + parsedStatement.getSqlWithoutComments()); + ErrorCode.INVALID_ARGUMENT, "Unknown statement: " + parsedStatement.getSql()); } @Override @@ -1699,7 +1697,7 @@ private ResultSet parseAndExecuteQuery( throw SpannerExceptionFactory.newSpannerException( ErrorCode.FAILED_PRECONDITION, "DML statement with returning clause cannot be executed in read-only mode: " - + parsedStatement.getSqlWithoutComments()); + + parsedStatement.getSql()); } return internalExecuteQuery(callType, parsedStatement, analyzeMode, options); } @@ -1710,8 +1708,7 @@ private ResultSet parseAndExecuteQuery( } throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, - "Statement is not a query or DML with returning clause: " - + parsedStatement.getSqlWithoutComments()); + "Statement is not a query or DML with returning clause: " + parsedStatement.getSql()); } private AsyncResultSet parseAndExecuteQueryAsync(Statement query, QueryOption... options) { @@ -1741,7 +1738,7 @@ private AsyncResultSet parseAndExecuteQueryAsync(Statement query, QueryOption... throw SpannerExceptionFactory.newSpannerException( ErrorCode.FAILED_PRECONDITION, "DML statement with returning clause cannot be executed in read-only mode: " - + parsedStatement.getSqlWithoutComments()); + + parsedStatement.getSql()); } return internalExecuteQueryAsync( CallType.ASYNC, parsedStatement, AnalyzeMode.NONE, options); @@ -1753,8 +1750,7 @@ private AsyncResultSet parseAndExecuteQueryAsync(Statement query, QueryOption... } throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, - "Statement is not a query or DML with returning clause: " - + parsedStatement.getSqlWithoutComments()); + "Statement is not a query or DML with returning clause: " + parsedStatement.getSql()); } private boolean isInternalMetadataQuery(QueryOption... options) { @@ -1781,7 +1777,7 @@ public long executeUpdate(Statement update) { throw SpannerExceptionFactory.newSpannerException( ErrorCode.FAILED_PRECONDITION, "DML statement with returning clause cannot be executed using executeUpdate: " - + parsedStatement.getSqlWithoutComments() + + parsedStatement.getSql() + ". Please use executeQuery instead."); } return get(internalExecuteUpdateAsync(CallType.SYNC, parsedStatement)); @@ -1794,7 +1790,7 @@ public long executeUpdate(Statement update) { } throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, - "Statement is not an update statement: " + parsedStatement.getSqlWithoutComments()); + "Statement is not an update statement: " + parsedStatement.getSql()); } @Override @@ -1809,7 +1805,7 @@ public ApiFuture executeUpdateAsync(Statement update) { throw SpannerExceptionFactory.newSpannerException( ErrorCode.FAILED_PRECONDITION, "DML statement with returning clause cannot be executed using executeUpdateAsync: " - + parsedStatement.getSqlWithoutComments() + + parsedStatement.getSql() + ". Please use executeQueryAsync instead."); } return internalExecuteUpdateAsync(CallType.ASYNC, parsedStatement); @@ -1822,7 +1818,7 @@ public ApiFuture executeUpdateAsync(Statement update) { } throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, - "Statement is not an update statement: " + parsedStatement.getSqlWithoutComments()); + "Statement is not an update statement: " + parsedStatement.getSql()); } @Override @@ -1845,7 +1841,7 @@ public ResultSetStats analyzeUpdate(Statement update, QueryAnalyzeMode analyzeMo } throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, - "Statement is not an update statement: " + parsedStatement.getSqlWithoutComments()); + "Statement is not an update statement: " + parsedStatement.getSql()); } @Override @@ -1867,7 +1863,7 @@ public ResultSet analyzeUpdateStatement( } throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, - "Statement is not an update statement: " + parsedStatement.getSqlWithoutComments()); + "Statement is not an update statement: " + parsedStatement.getSql()); } @Override @@ -1899,7 +1895,7 @@ private List parseUpdateStatements(Iterable updates) throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, "The batch update list contains a statement that is not an update statement: " - + parsedStatement.getSqlWithoutComments()); + + parsedStatement.getSql()); } } return parsedStatements; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java index c26ea372514..b8f4676fa76 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner.connection; +import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.Options.RpcPriority; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; @@ -36,6 +37,7 @@ *

The client side statements are defined in the ClientSideStatements.json file. */ interface ConnectionStatementExecutor { + Dialect getDialect(); StatementResult statementSetAutocommit(Boolean autocommit); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java index e66bc92dbdd..1f4d8f5cf22 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java @@ -154,6 +154,11 @@ ConnectionImpl getConnection() { return connection; } + @Override + public Dialect getDialect() { + return getConnection().getDialect(); + } + @Override public StatementResult statementSetAutocommit(Boolean autocommit) { Preconditions.checkNotNull(autocommit); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java index 813f5d6e45b..4a8d643b79c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java @@ -191,13 +191,11 @@ public ApiFuture executeDdlAsync(CallType callType, ParsedStatement ddl) { "The batch is no longer active and cannot be used for further statements"); Preconditions.checkArgument( ddl.getType() == StatementType.DDL, - "Only DDL statements are allowed. \"" - + ddl.getSqlWithoutComments() - + "\" is not a DDL-statement."); + "Only DDL statements are allowed. \"" + ddl.getSql() + "\" is not a DDL-statement."); Preconditions.checkArgument( - !DdlClient.isCreateDatabaseStatement(ddl.getSqlWithoutComments()), + !DdlClient.isCreateDatabaseStatement(dbClient.getDialect(), ddl.getSql()), "CREATE DATABASE is not supported in DDL batches."); - statements.add(ddl.getSqlWithoutComments()); + statements.add(ddl.getSql()); return ApiFutures.immediateFuture(null); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java index a3dc286acb4..ef7ad7e5cdb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java @@ -34,6 +34,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Supplier; /** * Convenience class for executing Data Definition Language statements on transactions that support @@ -41,12 +42,14 @@ */ class DdlClient { private final DatabaseAdminClient dbAdminClient; + private final Supplier dialectSupplier; private final String projectId; private final String instanceId; private final String databaseName; static class Builder { private DatabaseAdminClient dbAdminClient; + private Supplier dialectSupplier; private String projectId; private String instanceId; private String databaseName; @@ -59,6 +62,11 @@ Builder setDatabaseAdminClient(DatabaseAdminClient client) { return this; } + Builder setDialectSupplier(Supplier dialectSupplier) { + this.dialectSupplier = Preconditions.checkNotNull(dialectSupplier); + return this; + } + Builder setProjectId(String projectId) { Preconditions.checkArgument( !Strings.isNullOrEmpty(projectId), "Empty projectId is not allowed"); @@ -82,6 +90,7 @@ Builder setDatabaseName(String name) { DdlClient build() { Preconditions.checkState(dbAdminClient != null, "No DatabaseAdminClient specified"); + Preconditions.checkState(dialectSupplier != null, "No dialect supplier specified"); Preconditions.checkState(!Strings.isNullOrEmpty(projectId), "No ProjectId specified"); Preconditions.checkState(!Strings.isNullOrEmpty(instanceId), "No InstanceId specified"); Preconditions.checkArgument( @@ -96,6 +105,7 @@ static Builder newBuilder() { private DdlClient(Builder builder) { this.dbAdminClient = builder.dbAdminClient; + this.dialectSupplier = builder.dialectSupplier; this.projectId = builder.projectId; this.instanceId = builder.instanceId; this.databaseName = builder.databaseName; @@ -103,7 +113,7 @@ private DdlClient(Builder builder) { OperationFuture executeCreateDatabase( String createStatement, Dialect dialect) { - Preconditions.checkArgument(isCreateDatabaseStatement(createStatement)); + Preconditions.checkArgument(isCreateDatabaseStatement(dialect, createStatement)); return dbAdminClient.createDatabase( instanceId, createStatement, dialect, Collections.emptyList()); } @@ -116,7 +126,8 @@ OperationFuture executeDdl(String ddl, byte[] p /** Execute a list of DDL statements as one operation. */ OperationFuture executeDdl( List statements, byte[] protoDescriptors) { - if (statements.stream().anyMatch(DdlClient::isCreateDatabaseStatement)) { + if (statements.stream() + .anyMatch(sql -> isCreateDatabaseStatement(this.dialectSupplier.get(), sql))) { throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, "CREATE DATABASE is not supported in a DDL batch"); } @@ -130,11 +141,9 @@ OperationFuture executeDdl( } /** Returns true if the statement is a `CREATE DATABASE ...` statement. */ - static boolean isCreateDatabaseStatement(String statement) { - String[] tokens = statement.split("\\s+", 3); - return tokens.length >= 2 - && tokens[0].equalsIgnoreCase("CREATE") - && tokens[1].equalsIgnoreCase("DATABASE"); + static boolean isCreateDatabaseStatement(Dialect dialect, String statement) { + SimpleParser parser = new SimpleParser(dialect, statement); + return parser.eatKeyword("create", "database"); } void runWithRetryForMissingDefaultSequenceKind( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DmlBatch.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DmlBatch.java index 1f5e72acee2..c1df52a49ca 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DmlBatch.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DmlBatch.java @@ -204,9 +204,7 @@ public ApiFuture executeUpdateAsync( "The batch is no longer active and cannot be used for further statements"); Preconditions.checkArgument( update.getType() == StatementType.UPDATE, - "Only DML statements are allowed. \"" - + update.getSqlWithoutComments() - + "\" is not a DML-statement."); + "Only DML statements are allowed. \"" + update.getSql() + "\" is not a DML-statement."); long updateCount = getUpdateCount(); this.statements.add(update); this.updateCounts = Arrays.copyOf(this.updateCounts, this.updateCounts.length + 1); @@ -233,9 +231,7 @@ public ApiFuture executeBatchUpdateAsync( for (ParsedStatement update : updates) { Preconditions.checkArgument( update.getType() == StatementType.UPDATE, - "Only DML statements are allowed. \"" - + update.getSqlWithoutComments() - + "\" is not a DML-statement."); + "Only DML statements are allowed. \"" + update.getSql() + "\" is not a DML-statement."); } long[] updateCountArray = new long[Iterables.size(updates)]; Arrays.fill(updateCountArray, getUpdateCount()); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadWriteTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadWriteTransaction.java index 3e4e98b16c0..828c17141a4 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadWriteTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadWriteTransaction.java @@ -785,8 +785,7 @@ public ApiFuture executeBatchUpdateAsync( final List updateStatements = new LinkedList<>(); for (ParsedStatement update : updates) { Preconditions.checkArgument( - update.isUpdate(), - "Statement is not an update statement: " + update.getSqlWithoutComments()); + update.isUpdate(), "Statement is not an update statement: " + update.getSql()); updateStatements.add(update.getStatement()); } checkOrCreateValidTransaction(Iterables.getFirst(updates, null), callType); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SimpleParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SimpleParser.java index 66bf337f678..acfb4aa070a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SimpleParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SimpleParser.java @@ -277,6 +277,55 @@ boolean eatToken(char token) { return false; } + boolean eatKeyword(String... keywords) { + return eat(true, true, keywords); + } + + boolean eat(boolean skipWhitespaceBefore, boolean requireWhitespaceAfter, String... keywords) { + boolean result = true; + for (String keyword : keywords) { + result &= internalEat(keyword, skipWhitespaceBefore, requireWhitespaceAfter, true); + } + return result; + } + + private boolean internalEat( + String keyword, + boolean skipWhitespaceBefore, + boolean requireWhitespaceAfter, + boolean updatePos) { + int originalPos = pos; + if (skipWhitespaceBefore) { + skipWhitespaces(); + } + if (pos + keyword.length() > sql.length()) { + if (!updatePos) { + pos = originalPos; + } + return false; + } + if (sql.substring(pos, pos + keyword.length()).equalsIgnoreCase(keyword) + && (!requireWhitespaceAfter || isValidEndOfKeyword(pos + keyword.length()))) { + if (updatePos) { + pos = pos + keyword.length(); + } else { + pos = originalPos; + } + return true; + } + if (!updatePos) { + pos = originalPos; + } + return false; + } + + private boolean isValidEndOfKeyword(int index) { + if (sql.length() == index) { + return true; + } + return !isValidIdentifierChar(sql.charAt(index)); + } + /** * Returns true if the given character is valid as the first character of an identifier. That * means that it can be used as the first character of an unquoted identifier. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java index 123b71ff014..85cef17f9de 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java @@ -386,13 +386,13 @@ public ApiFuture executeDdlAsync(CallType callType, final ParsedStatement Callable callable = () -> { try { - if (isCreateDatabaseStatement(ddl.getSqlWithoutComments())) { + if (isCreateDatabaseStatement(dbClient.getDialect(), ddl.getSql())) { executeCreateDatabase(ddl); } else { ddlClient.runWithRetryForMissingDefaultSequenceKind( restartIndex -> { OperationFuture operation = - ddlClient.executeDdl(ddl.getSqlWithoutComments(), protoDescriptors); + ddlClient.executeDdl(ddl.getSql(), protoDescriptors); getWithStatementTimeout(operation, ddl); }, connectionState.getValue(DEFAULT_SEQUENCE_KIND).getValue(), @@ -413,7 +413,7 @@ public ApiFuture executeDdlAsync(CallType callType, final ParsedStatement private void executeCreateDatabase(ParsedStatement ddl) { OperationFuture operation = - ddlClient.executeCreateDatabase(ddl.getSqlWithoutComments(), dbClient.getDialect()); + ddlClient.executeCreateDatabase(ddl.getSql(), dbClient.getDialect()); getWithStatementTimeout(operation, ddl); } @@ -474,8 +474,7 @@ public ApiFuture executeBatchUpdateAsync( Preconditions.checkNotNull(updates); for (ParsedStatement update : updates) { Preconditions.checkArgument( - update.isUpdate(), - "Statement is not an update statement: " + update.getSqlWithoutComments()); + update.isUpdate(), "Statement is not an update statement: " + update.getSql()); } ConnectionPreconditions.checkState( !isReadOnly(), "Batch update statements are not allowed in read-only mode"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java index 0cf113cc888..4055f8e949c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.ErrorCode; @@ -153,6 +154,7 @@ public void testSetStatementTimeout() { new DurationTestData("set statement_timeout = " + resetValue + " ", Duration.ZERO), }) { ConnectionStatementExecutor executor = mock(ConnectionStatementExecutor.class); + when(executor.getDialect()).thenReturn(dialect); ParsedStatement statement = parser.parse(Statement.of(data.sql)); assertEquals( ClientSideStatementType.SET_STATEMENT_TIMEOUT, statement.getClientSideStatementType()); @@ -196,6 +198,7 @@ public void testSetMaxCommitDelay() { new DurationTestData("set " + prefix + "max_commit_delay = null ", Duration.ZERO), }) { ConnectionStatementExecutor executor = mock(ConnectionStatementExecutor.class); + when(executor.getDialect()).thenReturn(dialect); ParsedStatement statement = parser.parse(Statement.of(data.sql)); assertEquals( ClientSideStatementType.SET_MAX_COMMIT_DELAY, statement.getClientSideStatementType()); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java index 72a8e64ae4c..0c86da54de1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java @@ -64,6 +64,7 @@ public void testExecuteSetAutocommit() { ParsedStatement subject = parser.parse(Statement.of("set autocommit = true")); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetAutocommit(any(Boolean.class))).thenCallRealMethod(); for (Boolean mode : new Boolean[] {Boolean.FALSE, Boolean.TRUE}) { @@ -80,6 +81,7 @@ public void testExecuteSetReadOnly() { parser.parse(Statement.of(String.format("set %sreadonly = true", getNamespace(dialect)))); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetReadOnly(any(Boolean.class))).thenCallRealMethod(); for (Boolean mode : new Boolean[] {Boolean.FALSE, Boolean.TRUE}) { @@ -98,6 +100,7 @@ public void testExecuteSetReadOnlyTo() { parser.parse(Statement.of(String.format("set %sreadonly to true", getNamespace(dialect)))); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetReadOnly(any(Boolean.class))).thenCallRealMethod(); for (Boolean mode : new Boolean[] {Boolean.FALSE, Boolean.TRUE}) { @@ -116,6 +119,7 @@ public void testExecuteSetAutocommitDmlMode() { Statement.of(String.format("set %sautocommit_dml_mode='foo'", getNamespace(dialect)))); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetAutocommitDmlMode(any(AutocommitDmlMode.class))).thenCallRealMethod(); for (AutocommitDmlMode mode : AutocommitDmlMode.values()) { @@ -135,6 +139,7 @@ public void testExecuteSetStatementTimeout() { ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); when(executor.statementSetStatementTimeout(any(Duration.class))).thenCallRealMethod(); ConnectionImpl connection = mock(ConnectionImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); for (TimeUnit unit : ReadOnlyStalenessUtil.SUPPORTED_UNITS) { for (Long val : new Long[] {1L, 100L, 999L}) { @@ -173,6 +178,7 @@ public void testExecuteSetReadOnlyStaleness() { Statement.of(String.format("set %sread_only_staleness='foo'", getNamespace(dialect)))); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetReadOnlyStaleness(any(TimestampBound.class))).thenCallRealMethod(); for (TimestampBound val : @@ -219,6 +225,7 @@ public void testExecuteSetOptimizerVersion() { Statement.of(String.format("set %soptimizer_version='foo'", getNamespace(dialect)))); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetOptimizerVersion(any(String.class))).thenCallRealMethod(); for (String version : new String[] {"1", "200", "", "LATEST"}) { @@ -239,6 +246,7 @@ public void testExecuteSetOptimizerStatisticsPackage() { String.format("set %soptimizer_statistics_package='foo'", getNamespace(dialect)))); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getDialect()).thenReturn(dialect); when(executor.getConnection()).thenReturn(connection); when(executor.statementSetOptimizerStatisticsPackage(any(String.class))).thenCallRealMethod(); for (String statisticsPackage : new String[] {"custom-package", ""}) { @@ -259,6 +267,7 @@ public void testExecuteSetTransaction() { ParsedStatement subject = parser.parse(Statement.of("set transaction read_only")); ConnectionImpl connection = mock(ConnectionImpl.class); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); + when(executor.getDialect()).thenReturn(dialect); for (TransactionMode mode : TransactionMode.values()) { subject .getClientSideStatement() diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java index 4d582e09007..e500851b275 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java @@ -135,6 +135,7 @@ private DdlBatch createSubject(DdlClient ddlClient) { } private DdlBatch createSubject(DdlClient ddlClient, DatabaseClient dbClient) { + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); return DdlBatch.newBuilder() .setDdlClient(ddlClient) .setDatabaseClient(dbClient) @@ -273,7 +274,7 @@ public void testGetStateAndIsActive() { assertThat(batch.isActive(), is(true)); ParsedStatement statement = mock(ParsedStatement.class); when(statement.getStatement()).thenReturn(Statement.of("CREATE TABLE FOO")); - when(statement.getSqlWithoutComments()).thenReturn("CREATE TABLE FOO"); + when(statement.getSql()).thenReturn("CREATE TABLE FOO"); when(statement.getType()).thenReturn(StatementType.DDL); batch.executeDdlAsync(CallType.SYNC, statement); try { @@ -319,7 +320,7 @@ public void testRunBatch() { ParsedStatement statement = mock(ParsedStatement.class); when(statement.getType()).thenReturn(StatementType.DDL); when(statement.getStatement()).thenReturn(Statement.of("CREATE TABLE FOO")); - when(statement.getSqlWithoutComments()).thenReturn("CREATE TABLE FOO"); + when(statement.getSql()).thenReturn("CREATE TABLE FOO"); client = createDefaultMockDdlClient(); batch = createSubject(client); @@ -382,10 +383,12 @@ public void testRunBatch() { // verify when protoDescriptors is null client = createDefaultMockDdlClient(); + DatabaseClient dbClient = mock(DatabaseClient.class); + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); batch = DdlBatch.newBuilder() .setDdlClient(client) - .setDatabaseClient(mock(DatabaseClient.class)) + .setDatabaseClient(dbClient) .withStatementExecutor(new StatementExecutor()) .setSpan(Span.getInvalid()) .setProtoDescriptors(null) @@ -412,7 +415,7 @@ public void testRunBatch() { batch = DdlBatch.newBuilder() .setDdlClient(client) - .setDatabaseClient(mock(DatabaseClient.class)) + .setDatabaseClient(dbClient) .withStatementExecutor(new StatementExecutor()) .setSpan(Span.getInvalid()) .setProtoDescriptors(protoDescriptors) @@ -442,11 +445,13 @@ public void testUpdateCount() throws InterruptedException, ExecutionException { when(operationFuture.getMetadata()).thenReturn(metadataFuture); when(client.executeDdl(argThat(isListOfStringsWithSize(2)), isNull())) .thenReturn(operationFuture); + DatabaseClient dbClient = mock(DatabaseClient.class); + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); DdlBatch batch = DdlBatch.newBuilder() .withStatementExecutor(new StatementExecutor()) .setDdlClient(client) - .setDatabaseClient(mock(DatabaseClient.class)) + .setDatabaseClient(dbClient) .setSpan(Span.getInvalid()) .setConnectionState(new ConnectionState(new HashMap<>())) .build(); @@ -486,11 +491,13 @@ public void testFailedUpdateCount() throws InterruptedException, ExecutionExcept .runWithRetryForMissingDefaultSequenceKind(any(), any(), any(), any()); when(client.executeDdl(argThat(isListOfStringsWithSize(2)), isNull())) .thenReturn(operationFuture); + DatabaseClient dbClient = mock(DatabaseClient.class); + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); DdlBatch batch = DdlBatch.newBuilder() .withStatementExecutor(new StatementExecutor()) .setDdlClient(client) - .setDatabaseClient(mock(DatabaseClient.class)) + .setDatabaseClient(dbClient) .setSpan(Span.getInvalid()) .setConnectionState(new ConnectionState(new HashMap<>())) .build(); @@ -534,11 +541,13 @@ public void testFailedAfterFirstStatement() throws InterruptedException, Executi when(operationFuture.getMetadata()).thenReturn(metadataFuture); when(client.executeDdl(argThat(isListOfStringsWithSize(2)), isNull())) .thenReturn(operationFuture); + DatabaseClient dbClient = mock(DatabaseClient.class); + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); DdlBatch batch = DdlBatch.newBuilder() .withStatementExecutor(new StatementExecutor()) .setDdlClient(client) - .setDatabaseClient(mock(DatabaseClient.class)) + .setDatabaseClient(dbClient) .setSpan(Span.getInvalid()) .setConnectionState(new ConnectionState(new HashMap<>())) .build(); @@ -572,7 +581,7 @@ public void testAbort() { ParsedStatement statement = mock(ParsedStatement.class); when(statement.getType()).thenReturn(StatementType.DDL); when(statement.getStatement()).thenReturn(Statement.of("CREATE TABLE FOO")); - when(statement.getSqlWithoutComments()).thenReturn("CREATE TABLE FOO"); + when(statement.getSql()).thenReturn("CREATE TABLE FOO"); client = createDefaultMockDdlClient(); batch = createSubject(client); @@ -611,7 +620,7 @@ public void testCancel() { ParsedStatement statement = mock(ParsedStatement.class); when(statement.getType()).thenReturn(StatementType.DDL); when(statement.getStatement()).thenReturn(Statement.of("CREATE TABLE FOO")); - when(statement.getSqlWithoutComments()).thenReturn("CREATE TABLE FOO"); + when(statement.getSql()).thenReturn("CREATE TABLE FOO"); DdlClient client = createDefaultMockDdlClient(10000L); final DdlBatch batch = createSubject(client); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTests.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTests.java index c61635fce23..3a25437354f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTests.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTests.java @@ -32,7 +32,9 @@ import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.common.base.Suppliers; import com.google.common.io.ByteStreams; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.io.InputStream; @@ -53,6 +55,7 @@ public class DdlClientTests { private DdlClient createSubject(DatabaseAdminClient client) { return DdlClient.newBuilder() + .setDialectSupplier(Suppliers.ofInstance(Dialect.GOOGLE_STANDARD_SQL)) .setProjectId(projectId) .setInstanceId(instanceId) .setDatabaseName(databaseId) @@ -108,20 +111,22 @@ public void testExecuteDdl() throws InterruptedException, ExecutionException { @Test public void testIsCreateDatabase() { - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE foo")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE \"foo\"")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE `foo`")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE\tfoo")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE\n foo")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE\t\n foo")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE")); - assertTrue(DdlClient.isCreateDatabaseStatement("CREATE\t \n DATABASE foo")); - assertTrue(DdlClient.isCreateDatabaseStatement("create\t \n DATABASE foo")); - assertTrue(DdlClient.isCreateDatabaseStatement("create database foo")); + for (Dialect dialect : Dialect.values()) { + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE foo")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE \"foo\"")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE `foo`")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE\tfoo")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE\n foo")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE\t\n foo")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASE")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "CREATE\t \n DATABASE foo")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "create\t \n DATABASE foo")); + assertTrue(DdlClient.isCreateDatabaseStatement(dialect, "create database foo")); - assertFalse(DdlClient.isCreateDatabaseStatement("CREATE VIEW foo")); - assertFalse(DdlClient.isCreateDatabaseStatement("CREATE DATABAS foo")); - assertFalse(DdlClient.isCreateDatabaseStatement("CREATE DATABASEfoo")); - assertFalse(DdlClient.isCreateDatabaseStatement("CREATE foo")); + assertFalse(DdlClient.isCreateDatabaseStatement(dialect, "CREATE VIEW foo")); + assertFalse(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABAS foo")); + assertFalse(DdlClient.isCreateDatabaseStatement(dialect, "CREATE DATABASEfoo")); + assertFalse(DdlClient.isCreateDatabaseStatement(dialect, "CREATE foo")); + } } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DmlBatchTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DmlBatchTest.java index 629ae41daf4..ab04bb61a54 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DmlBatchTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DmlBatchTest.java @@ -177,7 +177,7 @@ public void testGetStateAndIsActive() { assertThat(batch.isActive(), is(true)); ParsedStatement statement = mock(ParsedStatement.class); when(statement.getStatement()).thenReturn(Statement.of("UPDATE TEST SET COL1=2")); - when(statement.getSqlWithoutComments()).thenReturn("UPDATE TEST SET COL1=2"); + when(statement.getSql()).thenReturn("UPDATE TEST SET COL1=2"); when(statement.getType()).thenReturn(StatementType.UPDATE); get(batch.executeUpdateAsync(CallType.SYNC, statement)); boolean exception = false; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java index 5feb10ebaaf..6a4731dbbda 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ITAbstractSpannerTest.java @@ -128,7 +128,7 @@ protected boolean shouldAbort(String statement, ExecutionStep step) { @Override public void intercept( ParsedStatement statement, StatementExecutionStep step, UnitOfWork transaction) { - if (shouldAbort(statement.getSqlWithoutComments(), ExecutionStep.of(step))) { + if (shouldAbort(statement.getSql(), ExecutionStep.of(step))) { // ugly hack warning: inject the aborted state into the transaction manager to simulate an // abort if (transaction instanceof ReadWriteTransaction) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadOnlyTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadOnlyTransactionTest.java index e243fbd620a..0c592d85804 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadOnlyTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadOnlyTransactionTest.java @@ -287,7 +287,7 @@ public void testExecuteQuery() { when(parsedStatement.isQuery()).thenReturn(true); Statement statement = Statement.of("SELECT * FROM FOO"); when(parsedStatement.getStatement()).thenReturn(statement); - when(parsedStatement.getSqlWithoutComments()).thenReturn(statement.getSql()); + when(parsedStatement.getSql()).thenReturn(statement.getSql()); ReadOnlyTransaction transaction = createSubject(staleness); ResultSet rs = @@ -306,7 +306,7 @@ public void testExecuteQueryWithOptionsTest() { when(parsedStatement.isQuery()).thenReturn(true); Statement statement = Statement.of(sql); when(parsedStatement.getStatement()).thenReturn(statement); - when(parsedStatement.getSqlWithoutComments()).thenReturn(statement.getSql()); + when(parsedStatement.getSql()).thenReturn(statement.getSql()); DatabaseClient client = mock(DatabaseClient.class); com.google.cloud.spanner.ReadOnlyTransaction tx = mock(com.google.cloud.spanner.ReadOnlyTransaction.class); @@ -344,7 +344,7 @@ public void testPlanQuery() { when(parsedStatement.isQuery()).thenReturn(true); Statement statement = Statement.of("SELECT * FROM FOO"); when(parsedStatement.getStatement()).thenReturn(statement); - when(parsedStatement.getSqlWithoutComments()).thenReturn(statement.getSql()); + when(parsedStatement.getSql()).thenReturn(statement.getSql()); ReadOnlyTransaction transaction = createSubject(staleness); ResultSet rs = @@ -366,7 +366,7 @@ public void testProfileQuery() { when(parsedStatement.isQuery()).thenReturn(true); Statement statement = Statement.of("SELECT * FROM FOO"); when(parsedStatement.getStatement()).thenReturn(statement); - when(parsedStatement.getSqlWithoutComments()).thenReturn(statement.getSql()); + when(parsedStatement.getSql()).thenReturn(statement.getSql()); ReadOnlyTransaction transaction = createSubject(staleness); ResultSet rs = @@ -388,7 +388,7 @@ public void testGetReadTimestamp() { when(parsedStatement.isQuery()).thenReturn(true); Statement statement = Statement.of("SELECT * FROM FOO"); when(parsedStatement.getStatement()).thenReturn(statement); - when(parsedStatement.getSqlWithoutComments()).thenReturn(statement.getSql()); + when(parsedStatement.getSql()).thenReturn(statement.getSql()); ReadOnlyTransaction transaction = createSubject(staleness); boolean expectedException = false; @@ -423,7 +423,7 @@ public void testState() { when(parsedStatement.isQuery()).thenReturn(true); Statement statement = Statement.of("SELECT * FROM FOO"); when(parsedStatement.getStatement()).thenReturn(statement); - when(parsedStatement.getSqlWithoutComments()).thenReturn(statement.getSql()); + when(parsedStatement.getSql()).thenReturn(statement.getSql()); ReadOnlyTransaction transaction = createSubject(); assertThat( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SetPgSessionCharacteristicsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SetPgSessionCharacteristicsTest.java index 96381281ca5..4b4331e5f3a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SetPgSessionCharacteristicsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SetPgSessionCharacteristicsTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.Statement; @@ -41,6 +42,7 @@ public class SetPgSessionCharacteristicsTest { @Test public void testSetIsolationLevelDefault() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = "set session characteristics as transaction isolation level default"; @@ -55,6 +57,7 @@ public void testSetIsolationLevelDefault() { @Test public void testSetIsolationLevelSerializable() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = "set session characteristics as transaction isolation level serializable"; @@ -69,6 +72,7 @@ public void testSetIsolationLevelSerializable() { @Test public void testSetIsolationLevelRepeatableRead() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = "set session characteristics as transaction isolation level repeatable read"; @@ -83,6 +87,7 @@ public void testSetIsolationLevelRepeatableRead() { @Test public void testSetIsolationLevelReadOnly() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = "set\tsession\ncharacteristics as transaction read only"; @@ -98,6 +103,7 @@ public void testSetIsolationLevelReadOnly() { @Test public void testSetIsolationLevelReadWrite() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = "set session characteristics as transaction read write"; @@ -113,6 +119,7 @@ public void testSetIsolationLevelReadWrite() { @Test public void testSetIsolationLevelSerializableReadWrite() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = @@ -129,6 +136,7 @@ public void testSetIsolationLevelSerializableReadWrite() { @Test public void testSetIsolationLevelSerializableReadOnly() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = @@ -144,6 +152,7 @@ public void testSetIsolationLevelSerializableReadOnly() { @Test public void testSetMultipleTransactionModes() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String sql = @@ -160,6 +169,7 @@ public void testSetMultipleTransactionModes() { @Test public void testDefaultTransactionIsolation() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); int count = 0; @@ -189,6 +199,7 @@ public void testDefaultTransactionIsolation() { @Test public void testDefaultTransactionReadOnlyTrue() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String[] statements = new String[] { @@ -223,6 +234,7 @@ public void testDefaultTransactionReadOnlyTrue() { @Test public void testDefaultTransactionReadOnlyFalse() { ConnectionImpl connection = mock(ConnectionImpl.class); + when(connection.getDialect()).thenReturn(Dialect.POSTGRESQL); ConnectionStatementExecutorImpl executor = new ConnectionStatementExecutorImpl(connection); String[] statements = new String[] { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java index 5ac926d5956..76ace88d77a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java @@ -500,7 +500,7 @@ private ParsedStatement createParsedDdl(String sql) { ParsedStatement statement = mock(ParsedStatement.class); when(statement.getType()).thenReturn(StatementType.DDL); when(statement.getStatement()).thenReturn(Statement.of(sql)); - when(statement.getSqlWithoutComments()).thenReturn(sql); + when(statement.getSql()).thenReturn(sql); return statement; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java index fbb0c024592..529ee258217 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java @@ -1713,6 +1713,16 @@ public void testStatementCache_ParameterizedStatement() { assertEquals(1, stats.hitCount()); } + @Test + public void testClientSideStatementWithComment() { + String sql = "-- Null (no timeout)\n" + "SET STATEMENT_TIMEOUT=null"; + ParsedStatement parsedStatement = parser.parse(Statement.of(sql)); + assertEquals(StatementType.CLIENT_SIDE, parsedStatement.getType()); + assertEquals( + ClientSideStatementType.SET_STATEMENT_TIMEOUT, + parsedStatement.getClientSideStatementType()); + } + static void assertUnclosedLiteral(AbstractStatementParser parser, String sql) { SpannerException exception = assertThrows( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy