diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java index a7752ab6671..b8ed22b3f3a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementValueConverters.java @@ -25,9 +25,11 @@ import com.google.cloud.spanner.connection.PgTransactionMode.IsolationLevel; import com.google.common.base.Function; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.protobuf.Duration; import com.google.protobuf.util.Durations; import com.google.spanner.v1.RequestOptions.Priority; +import java.util.Base64; import java.util.EnumSet; import java.util.HashMap; import java.util.Locale; @@ -494,4 +496,46 @@ public String convert(String value) { return value.substring(7).trim(); } } + + /** Converter for converting Base64 encoded string to byte[] */ + static class ProtoDescriptorsConverter implements ClientSideStatementValueConverter { + + public ProtoDescriptorsConverter(String allowedValues) {} + + @Override + public Class getParameterClass() { + return byte[].class; + } + + @Override + public byte[] convert(String value) { + if (value == null || value.length() == 0 || value.equalsIgnoreCase("null")) { + return null; + } + try { + return Base64.getDecoder().decode(value); + } catch (IllegalArgumentException e) { + return null; + } + } + } + + /** Converter for converting String that take in file path as input to String */ + static class ProtoDescriptorsFileConverter implements ClientSideStatementValueConverter { + + public ProtoDescriptorsFileConverter(String allowedValues) {} + + @Override + public Class getParameterClass() { + return String.class; + } + + @Override + public String convert(String filePath) { + if (Strings.isNullOrEmpty(filePath)) { + return null; + } + return filePath; + } + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java index d4624bfabbb..e864180420d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java @@ -42,6 +42,7 @@ import java.util.Iterator; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; /** * Internal connection API for Google Cloud Spanner. This interface may introduce breaking changes @@ -382,6 +383,25 @@ default String getStatementTag() { throw new UnsupportedOperationException(); } + /** + * Sets the proto descriptors to use for the next DDL statement (single or batch) that will be + * executed. The proto descriptor is automatically cleared after the statement is executed. + * + * @param protoDescriptors The proto descriptors to use with the next DDL statement (single or + * batch) that will be executed on this connection. + */ + default void setProtoDescriptors(@Nonnull byte[] protoDescriptors) { + throw new UnsupportedOperationException(); + } + + /** + * @return The proto descriptor that will be used with the next DDL statement (single or batch) + * that is executed on this connection. + */ + default byte[] getProtoDescriptors() { + throw new UnsupportedOperationException(); + } + /** * @return true if this connection will automatically retry read/write transactions * that abort. This method may only be called when the connection is in read/write 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 b6e29dbdc0e..1d4c6bb4d7a 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 @@ -21,6 +21,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.cloud.ByteArray; import com.google.cloud.Timestamp; import com.google.cloud.spanner.AsyncResultSet; import com.google.cloud.spanner.CommitResponse; @@ -51,6 +52,9 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import com.google.spanner.v1.ResultSetStats; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -63,6 +67,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.annotation.Nonnull; import org.threeten.bp.Instant; /** Implementation for {@link Connection}, the generic Spanner connection API (not JDBC). */ @@ -85,7 +90,7 @@ private LeakedConnectionException() { } } - private volatile LeakedConnectionException leakedException;; + private volatile LeakedConnectionException leakedException; private final SpannerPool spannerPool; private AbstractStatementParser statementParser; /** @@ -221,6 +226,9 @@ static UnitOfWorkType of(TransactionMode transactionMode) { private String transactionTag; private String statementTag; + private byte[] protoDescriptors; + private String protoDescriptorsFilePath; + /** Create a connection and register it in the SpannerPool. */ ConnectionImpl(ConnectionOptions options) { Preconditions.checkNotNull(options); @@ -278,6 +286,7 @@ Spanner getSpanner() { private DdlClient createDdlClient() { return DdlClient.newBuilder() .setDatabaseAdminClient(spanner.getDatabaseAdminClient()) + .setProjectId(options.getProjectId()) .setInstanceId(options.getInstanceId()) .setDatabaseName(options.getDatabaseName()) .build(); @@ -623,6 +632,52 @@ public void setStatementTag(String tag) { this.statementTag = tag; } + @Override + public byte[] getProtoDescriptors() { + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + if (this.protoDescriptors == null && this.protoDescriptorsFilePath != null) { + // Read from file if filepath is valid + try { + File protoDescriptorsFile = new File(this.protoDescriptorsFilePath); + if (!protoDescriptorsFile.isFile()) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + String.format( + "File %s is not a valid proto descriptors file", this.protoDescriptorsFilePath)); + } + InputStream pdStream = new FileInputStream(protoDescriptorsFile); + this.protoDescriptors = ByteArray.copyFrom(pdStream).toByteArray(); + } catch (Exception exception) { + throw SpannerExceptionFactory.newSpannerException(exception); + } + } + return this.protoDescriptors; + } + + @Override + public void setProtoDescriptors(@Nonnull byte[] protoDescriptors) { + Preconditions.checkNotNull(protoDescriptors); + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + ConnectionPreconditions.checkState( + !isBatchActive(), "Proto descriptors cannot be set when a batch is active"); + this.protoDescriptors = protoDescriptors; + this.protoDescriptorsFilePath = null; + } + + void setProtoDescriptorsFilePath(@Nonnull String protoDescriptorsFilePath) { + Preconditions.checkNotNull(protoDescriptorsFilePath); + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + ConnectionPreconditions.checkState( + !isBatchActive(), "Proto descriptors file path cannot be set when a batch is active"); + this.protoDescriptorsFilePath = protoDescriptorsFilePath; + this.protoDescriptors = null; + } + + String getProtoDescriptorsFilePath() { + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + return this.protoDescriptorsFilePath; + } + /** * Throws an {@link SpannerException} with code {@link ErrorCode#FAILED_PRECONDITION} if the * current state of this connection does not allow changing the setting for retryAbortsInternally. @@ -1379,6 +1434,7 @@ UnitOfWork createNewUnitOfWork() { .setReturnCommitStats(returnCommitStats) .setStatementTimeout(statementTimeout) .withStatementExecutor(statementExecutor) + .setProtoDescriptors(getProtoDescriptors()) .build(); } else { switch (getUnitOfWorkType()) { @@ -1421,6 +1477,7 @@ UnitOfWork createNewUnitOfWork() { .setDatabaseClient(dbClient) .setStatementTimeout(statementTimeout) .withStatementExecutor(statementExecutor) + .setProtoDescriptors(getProtoDescriptors()) .build(); default: } @@ -1444,7 +1501,12 @@ private void popUnitOfWorkFromTransactionStack() { } private ApiFuture executeDdlAsync(CallType callType, ParsedStatement ddl) { - return getCurrentUnitOfWorkOrStartNewUnitOfWork().executeDdlAsync(callType, ddl); + ApiFuture result = + getCurrentUnitOfWorkOrStartNewUnitOfWork().executeDdlAsync(callType, ddl); + // reset proto descriptors after executing a DDL statement + this.protoDescriptors = null; + this.protoDescriptorsFilePath = null; + return result; } @Override @@ -1535,6 +1597,11 @@ public ApiFuture runBatchAsync() { } return ApiFutures.immediateFuture(new long[0]); } finally { + if (isDdlBatchActive()) { + // reset proto descriptors after executing a DDL batch + this.protoDescriptors = null; + this.protoDescriptorsFilePath = null; + } this.batchMode = BatchMode.NONE; setDefaultTransactionOptions(); } 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 718025e1652..40350dfef7a 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 @@ -124,5 +124,13 @@ StatementResult statementSetPgSessionCharacteristicsTransactionMode( StatementResult statementShowTransactionIsolationLevel(); + StatementResult statementSetProtoDescriptors(byte[] protoDescriptors); + + StatementResult statementSetProtoDescriptorsFilePath(String filePath); + + StatementResult statementShowProtoDescriptors(); + + StatementResult statementShowProtoDescriptorsFilePath(); + StatementResult statementExplain(String sql); } 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 4bda03367af..d3854027d9e 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 @@ -28,6 +28,8 @@ import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_STATISTICS_PACKAGE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_VERSION; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_PROTO_DESCRIPTORS; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_PROTO_DESCRIPTORS_FILE_PATH; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_READONLY; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_READ_ONLY_STALENESS; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_RETRY_ABORTS_INTERNALLY; @@ -45,6 +47,8 @@ import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_DELAY_TRANSACTION_START_UNTIL_FIRST_WRITE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_STATISTICS_PACKAGE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_VERSION; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_PROTO_DESCRIPTORS; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_PROTO_DESCRIPTORS_FILE_PATH; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READONLY; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READ_ONLY_STALENESS; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READ_TIMESTAMP; @@ -493,6 +497,36 @@ public StatementResult statementShowTransactionIsolationLevel() { return resultSet("transaction_isolation", "serializable", SHOW_TRANSACTION_ISOLATION_LEVEL); } + @Override + public StatementResult statementSetProtoDescriptors(byte[] protoDescriptors) { + Preconditions.checkNotNull(protoDescriptors); + getConnection().setProtoDescriptors(protoDescriptors); + return noResult(SET_PROTO_DESCRIPTORS); + } + + @Override + public StatementResult statementSetProtoDescriptorsFilePath(String filePath) { + Preconditions.checkNotNull(filePath); + getConnection().setProtoDescriptorsFilePath(filePath); + return noResult(SET_PROTO_DESCRIPTORS_FILE_PATH); + } + + @Override + public StatementResult statementShowProtoDescriptors() { + return resultSet( + String.format("%sPROTO_DESCRIPTORS", getNamespace(connection.getDialect())), + getConnection().getProtoDescriptors(), + SHOW_PROTO_DESCRIPTORS); + } + + @Override + public StatementResult statementShowProtoDescriptorsFilePath() { + return resultSet( + String.format("%sPROTO_DESCRIPTORS_FILE_PATH", getNamespace(connection.getDialect())), + getConnection().getProtoDescriptorsFilePath(), + SHOW_PROTO_DESCRIPTORS_FILE_PATH); + } + private String processQueryPlan(PlanNode planNode) { StringBuilder planNodeDescription = new StringBuilder(" : { "); com.google.protobuf.Struct metadata = planNode.getMetadata(); 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 ebfd2bc4541..91ba90fdb12 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 @@ -58,10 +58,12 @@ class DdlBatch extends AbstractBaseUnitOfWork { private final DatabaseClient dbClient; private final List statements = new ArrayList<>(); private UnitOfWorkState state = UnitOfWorkState.STARTED; + private final byte[] protoDescriptors; static class Builder extends AbstractBaseUnitOfWork.Builder { private DdlClient ddlClient; private DatabaseClient dbClient; + private byte[] protoDescriptors; private Builder() {} @@ -77,6 +79,11 @@ Builder setDatabaseClient(DatabaseClient client) { return this; } + Builder setProtoDescriptors(byte[] protoDescriptors) { + this.protoDescriptors = protoDescriptors; + return this; + } + @Override DdlBatch build() { Preconditions.checkState(ddlClient != null, "No DdlClient specified"); @@ -93,6 +100,7 @@ private DdlBatch(Builder builder) { super(builder); this.ddlClient = builder.ddlClient; this.dbClient = builder.dbClient; + this.protoDescriptors = builder.protoDescriptors; } @Override @@ -239,7 +247,7 @@ public ApiFuture runBatchAsync(CallType callType) { () -> { try { OperationFuture operation = - ddlClient.executeDdl(statements); + ddlClient.executeDdl(statements, protoDescriptors); try { // Wait until the operation has finished. getWithStatementTimeout(operation, RUN_BATCH_STATEMENT); 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 fedf60d7a91..7bce1ab78cd 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 @@ -19,6 +19,7 @@ import com.google.api.gax.longrunning.OperationFuture; 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.ErrorCode; import com.google.cloud.spanner.SpannerExceptionFactory; @@ -35,11 +36,13 @@ */ class DdlClient { private final DatabaseAdminClient dbAdminClient; + private final String projectId; private final String instanceId; private final String databaseName; static class Builder { private DatabaseAdminClient dbAdminClient; + private String projectId; private String instanceId; private String databaseName; @@ -51,6 +54,13 @@ Builder setDatabaseAdminClient(DatabaseAdminClient client) { return this; } + Builder setProjectId(String projectId) { + Preconditions.checkArgument( + !Strings.isNullOrEmpty(projectId), "Empty projectId is not allowed"); + this.projectId = projectId; + return this; + } + Builder setInstanceId(String instanceId) { Preconditions.checkArgument( !Strings.isNullOrEmpty(instanceId), "Empty instanceId is not allowed"); @@ -67,6 +77,7 @@ Builder setDatabaseName(String name) { DdlClient build() { Preconditions.checkState(dbAdminClient != null, "No DatabaseAdminClient specified"); + Preconditions.checkState(!Strings.isNullOrEmpty(projectId), "No ProjectId specified"); Preconditions.checkState(!Strings.isNullOrEmpty(instanceId), "No InstanceId specified"); Preconditions.checkArgument( !Strings.isNullOrEmpty(databaseName), "No database name specified"); @@ -80,6 +91,7 @@ static Builder newBuilder() { private DdlClient(Builder builder) { this.dbAdminClient = builder.dbAdminClient; + this.projectId = builder.projectId; this.instanceId = builder.instanceId; this.databaseName = builder.databaseName; } @@ -92,17 +104,24 @@ OperationFuture executeCreateDatabase( } /** Execute a single DDL statement. */ - OperationFuture executeDdl(String ddl) { - return executeDdl(Collections.singletonList(ddl)); + OperationFuture executeDdl(String ddl, byte[] protoDescriptors) { + return executeDdl(Collections.singletonList(ddl), protoDescriptors); } /** Execute a list of DDL statements as one operation. */ - OperationFuture executeDdl(List statements) { + OperationFuture executeDdl( + List statements, byte[] protoDescriptors) { if (statements.stream().anyMatch(DdlClient::isCreateDatabaseStatement)) { throw SpannerExceptionFactory.newSpannerException( ErrorCode.INVALID_ARGUMENT, "CREATE DATABASE is not supported in a DDL batch"); } - return dbAdminClient.updateDatabaseDdl(instanceId, databaseName, statements, null); + Database.Builder dbBuilder = + dbAdminClient.newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseName)); + if (protoDescriptors != null) { + dbBuilder.setProtoDescriptors(protoDescriptors); + } + Database db = dbBuilder.build(); + return dbAdminClient.updateDatabaseDdl(db, statements, null); } /** Returns true if the statement is a `CREATE DATABASE ...` statement. */ 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 0e3dfe02efb..dfac432f4db 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 @@ -73,6 +73,7 @@ class SingleUseTransaction extends AbstractBaseUnitOfWork { private final TimestampBound readOnlyStaleness; private final AutocommitDmlMode autocommitDmlMode; private final boolean returnCommitStats; + private final byte[] protoDescriptors; private volatile SettableApiFuture readTimestamp = null; private volatile TransactionRunner writeTransaction; private boolean used = false; @@ -85,6 +86,7 @@ static class Builder extends AbstractBaseUnitOfWork.Builder executeDdlAsync(CallType callType, final ParsedStatement ddlClient.executeCreateDatabase( ddl.getSqlWithoutComments(), dbClient.getDialect()); } else { - operation = ddlClient.executeDdl(ddl.getSqlWithoutComments()); + operation = ddlClient.executeDdl(ddl.getSqlWithoutComments(), protoDescriptors); } getWithStatementTimeout(operation, ddl); state = UnitOfWorkState.COMMITTED; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java index f4e9237e090..cc09f62dbea 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java @@ -89,7 +89,11 @@ enum ClientSideStatementType { SHOW_TRANSACTION_ISOLATION_LEVEL, SHOW_SAVEPOINT_SUPPORT, SET_SAVEPOINT_SUPPORT, - EXPLAIN + EXPLAIN, + SET_PROTO_DESCRIPTORS, + SET_PROTO_DESCRIPTORS_FILE_PATH, + SHOW_PROTO_DESCRIPTORS, + SHOW_PROTO_DESCRIPTORS_FILE_PATH } /** diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResultImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResultImpl.java index 58a1f7ae1c0..ee5032463cd 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResultImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResultImpl.java @@ -18,6 +18,7 @@ import static com.google.cloud.spanner.SpannerApiFutures.get; +import com.google.cloud.ByteArray; import com.google.cloud.Timestamp; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.ResultSets; @@ -147,6 +148,23 @@ static StatementResult resultSet( clientSideStatementType); } + /** + * Convenience method for creating a {@link StatementResult} containing a {@link ResultSet} with + * one BYTES column and one row that is created by a {@link ClientSideStatement}. + */ + static StatementResult resultSet( + String name, byte[] values, ClientSideStatementType clientSideStatementType) { + return of( + ResultSets.forRows( + Type.struct(StructField.of(name, Type.bytes())), + Collections.singletonList( + Struct.newBuilder() + .set(name) + .to(values != null ? ByteArray.copyFrom(values) : null) + .build())), + clientSideStatementType); + } + /** {@link StatementResult} containing no results. */ static StatementResult noResult() { return new StatementResultImpl((ClientSideStatementType) null); diff --git a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json index 0f58ee951af..f726b8181c7 100644 --- a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json +++ b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json @@ -474,6 +474,54 @@ "allowedValues": "(TRUE|FALSE)", "converterName": "ClientSideStatementValueConverters$BooleanConverter" } + }, + { + "name": "SET PROTO_DESCRIPTORS = ''", + "executorName": "ClientSideStatementSetExecutor", + "resultType": "NO_RESULT", + "statementType": "SET_PROTO_DESCRIPTORS", + "regex": "(?is)\\A\\s*set\\s+proto_descriptors\\s*(?:=)\\s*(.*)\\z", + "method": "statementSetProtoDescriptors", + "exampleStatements": ["set proto_descriptors='protodescriptorsbase64'"], + "setStatement": { + "propertyName": "PROTO_DESCRIPTORS", + "separator": "=", + "allowedValues": "'((\\S+)|())'", + "converterName": "ClientSideStatementValueConverters$ProtoDescriptorsConverter" + } + }, + { + "name": "SET PROTO_DESCRIPTORS_FILE_PATH = ''", + "executorName": "ClientSideStatementSetExecutor", + "resultType": "NO_RESULT", + "statementType": "SET_PROTO_DESCRIPTORS_FILE_PATH", + "regex": "(?is)\\A\\s*set\\s+proto_descriptors_file_path\\s*(?:=)\\s*(.*)\\z", + "method": "statementSetProtoDescriptorsFilePath", + "exampleStatements": ["set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'"], + "setStatement": { + "propertyName": "PROTO_DESCRIPTORS_FILE_PATH", + "separator": "=", + "allowedValues": "'((\\S+)|())'", + "converterName": "ClientSideStatementValueConverters$ProtoDescriptorsFileConverter" + } + }, + { + "name": "SHOW VARIABLE PROTO_DESCRIPTORS", + "executorName": "ClientSideStatementNoParamExecutor", + "resultType": "RESULT_SET", + "statementType": "SHOW_PROTO_DESCRIPTORS", + "regex": "(?is)\\A\\s*show\\s+variable\\s+proto_descriptors\\s*\\z", + "method": "statementShowProtoDescriptors", + "exampleStatements": ["show variable proto_descriptors"] + }, + { + "name": "SHOW VARIABLE PROTO_DESCRIPTORS_FILE_PATH", + "executorName": "ClientSideStatementNoParamExecutor", + "resultType": "RESULT_SET", + "statementType": "SHOW_PROTO_DESCRIPTORS_FILE_PATH", + "regex": "(?is)\\A\\s*show\\s+variable\\s+proto_descriptors_file_path\\s*\\z", + "method": "statementShowProtoDescriptorsFilePath", + "exampleStatements": ["show variable proto_descriptors_file_path"] } ] } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index cd72da4cd3f..c0951bb8cad 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -24,12 +24,14 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyString; @@ -70,9 +72,11 @@ import com.google.cloud.spanner.connection.StatementResult.ResultType; import com.google.cloud.spanner.connection.UnitOfWork.CallType; import com.google.cloud.spanner.connection.UnitOfWork.UnitOfWorkState; +import com.google.common.io.ByteStreams; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import com.google.spanner.v1.ResultSetStats; +import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -213,8 +217,8 @@ private static DdlClient createDefaultMockDdlClient() { UpdateDatabaseDdlMetadata metadata = UpdateDatabaseDdlMetadata.getDefaultInstance(); ApiFuture futureMetadata = ApiFutures.immediateFuture(metadata); when(operation.getMetadata()).thenReturn(futureMetadata); - when(ddlClient.executeDdl(anyString())).thenCallRealMethod(); - when(ddlClient.executeDdl(anyList())).thenReturn(operation); + when(ddlClient.executeDdl(anyString(), isNull())).thenCallRealMethod(); + when(ddlClient.executeDdl(anyList(), isNull())).thenReturn(operation); return ddlClient; } catch (Exception e) { throw new RuntimeException(e); @@ -1610,4 +1614,99 @@ UnitOfWork createNewUnitOfWork() { assertNull(connection.getTransactionTag()); } } + + @Test + public void testProtoDescriptorsAlwaysAllowed() { + ConnectionOptions connectionOptions = mock(ConnectionOptions.class); + when(connectionOptions.isAutocommit()).thenReturn(true); + SpannerPool spannerPool = mock(SpannerPool.class); + DdlClient ddlClient = mock(DdlClient.class); + DatabaseClient dbClient = mock(DatabaseClient.class); + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); + final UnitOfWork unitOfWork = mock(UnitOfWork.class); + final String protoDescriptorsFilePath = + "src/test/resources/com/google/cloud/spanner/descriptors.pb"; + when(unitOfWork.executeDdlAsync(any(), any(ParsedStatement.class))) + .thenReturn(ApiFutures.immediateFuture(null)); + when(unitOfWork.executeQueryAsync( + any(), any(ParsedStatement.class), any(AnalyzeMode.class), Mockito.any())) + .thenReturn(ApiFutures.immediateFuture(mock(ResultSet.class))); + try (ConnectionImpl connection = + new ConnectionImpl(connectionOptions, spannerPool, ddlClient, dbClient) { + @Override + UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork() { + return unitOfWork; + } + }) { + byte[] protoDescriptors; + try { + InputStream in = + ConnectionImplTest.class + .getClassLoader() + .getResourceAsStream("com/google/cloud/spanner/descriptors.pb"); + assertNotNull(in); + protoDescriptors = ByteStreams.toByteArray(in); + } catch (Exception e) { + throw SpannerExceptionFactory.newSpannerException(e); + } + + assertTrue(connection.isAutocommit()); + + assertNull(connection.getProtoDescriptors()); + connection.setProtoDescriptors(protoDescriptors); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + + connection.setAutocommit(false); + + connection.setProtoDescriptors(protoDescriptors); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + + // proto descriptor should reset after executing a DDL statement + connection.setProtoDescriptors(protoDescriptors); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + connection.execute(Statement.of("CREATE PROTO BUNDLE (spanner.examples.music.SingerInfo)")); + assertNull(connection.getProtoDescriptors()); + + // proto descriptor should not reset if the statement is not a DDL statement + connection.setProtoDescriptors(protoDescriptors); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + connection.execute(Statement.of("SELECT FOO FROM BAR")); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + + // proto descriptor file path should reset after executing a DDL statement + connection.setProtoDescriptorsFilePath(protoDescriptorsFilePath); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + connection.execute(Statement.of("CREATE PROTO BUNDLE (spanner.examples.music.SingerInfo)")); + assertNull(connection.getProtoDescriptors()); + assertNull(connection.getProtoDescriptorsFilePath()); + + // proto descriptor file path should not reset if the statement is not a DDL statement + connection.setProtoDescriptorsFilePath(protoDescriptorsFilePath); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + connection.execute(Statement.of("SELECT FOO FROM BAR")); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + assertEquals(protoDescriptorsFilePath, connection.getProtoDescriptorsFilePath()); + + // test proto descriptor file path as input + connection.setProtoDescriptorsFilePath(protoDescriptorsFilePath); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + connection.execute(Statement.of("CREATE PROTO BUNDLE (spanner.examples.music.SingerInfo)")); + assertNull(connection.getProtoDescriptors()); + + // proto descriptor set through file path should overwrite the proto descriptor set from + // byte[] + connection.setProtoDescriptors("protoDescriptors".getBytes()); + connection.setProtoDescriptorsFilePath(protoDescriptorsFilePath); + assertArrayEquals(protoDescriptors, connection.getProtoDescriptors()); + connection.execute(Statement.of("CREATE PROTO BUNDLE (spanner.examples.music.SingerInfo)")); + assertNull(connection.getProtoDescriptors()); + + // proto descriptor set through byte[] should overwrite the proto descriptor from file path + connection.setProtoDescriptorsFilePath(protoDescriptorsFilePath); + connection.setProtoDescriptors("protoDescriptors".getBytes()); + assertArrayEquals("protoDescriptors".getBytes(), connection.getProtoDescriptors()); + connection.execute(Statement.of("CREATE PROTO BUNDLE (spanner.examples.music.SingerInfo)")); + assertNull(connection.getProtoDescriptors()); + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java index bf7822e3285..3e883c377fe 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java @@ -252,4 +252,29 @@ public void testStatementSetPgTransactionModeNoOp() { verify(connection, never()).setTransactionMode(TransactionMode.READ_ONLY_TRANSACTION); verify(connection, never()).setTransactionMode(TransactionMode.READ_WRITE_TRANSACTION); } + + @Test + public void testStatementSetProtoDescriptors() { + subject.statementSetProtoDescriptors("protoDescriptor".getBytes()); + verify(connection).setProtoDescriptors("protoDescriptor".getBytes()); + } + + @Test + public void testStatementSetProtoDescriptorsFilePath() { + String filePath = "com/google/cloud/spanner/descriptors.pb"; + subject.statementSetProtoDescriptorsFilePath(filePath); + verify(connection).setProtoDescriptorsFilePath(filePath); + } + + @Test + public void testStatementGetProtoDescriptors() { + subject.statementShowProtoDescriptors(); + verify(connection).getProtoDescriptors(); + } + + @Test + public void testStatementGetProtoDescriptorsFilePath() { + subject.statementShowProtoDescriptorsFilePath(); + verify(connection).getProtoDescriptorsFilePath(); + } } 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 699ab23f3a4..12301d7b15c 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 @@ -21,9 +21,12 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.argThat; @@ -51,9 +54,11 @@ import com.google.cloud.spanner.connection.Connection.InternalMetadataQuery; import com.google.cloud.spanner.connection.UnitOfWork.CallType; import com.google.cloud.spanner.connection.UnitOfWork.UnitOfWorkState; +import com.google.common.io.ByteStreams; import com.google.protobuf.Timestamp; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import io.grpc.Status; +import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -110,8 +115,8 @@ private DdlClient createDefaultMockDdlClient( ApiFuture metadataFuture = ApiFutures.immediateFuture(metadataBuilder.build()); when(operation.getMetadata()).thenReturn(metadataFuture); - when(ddlClient.executeDdl(anyString())).thenReturn(operation); - when(ddlClient.executeDdl(anyList())).thenReturn(operation); + when(ddlClient.executeDdl(anyString(), any())).thenReturn(operation); + when(ddlClient.executeDdl(anyList(), any())).thenReturn(operation); return ddlClient; } catch (Exception e) { throw new RuntimeException(e); @@ -273,7 +278,7 @@ public void testGetStateAndIsActive() { DdlClient client = mock(DdlClient.class); SpannerException exception = mock(SpannerException.class); when(exception.getErrorCode()).thenReturn(ErrorCode.FAILED_PRECONDITION); - doThrow(exception).when(client).executeDdl(anyList()); + doThrow(exception).when(client).executeDdl(anyList(), isNull()); batch = createSubject(client); assertThat(batch.getState(), is(UnitOfWorkState.STARTED)); assertThat(batch.isActive(), is(true)); @@ -319,8 +324,8 @@ public void testRunBatch() { DdlBatch batch = createSubject(client); get(batch.runBatchAsync(CallType.SYNC)); assertThat(batch.getState(), is(UnitOfWorkState.RAN)); - verify(client, never()).executeDdl(anyString()); - verify(client, never()).executeDdl(argThat(isEmptyListOfStrings())); + verify(client, never()).executeDdl(anyString(), isNull()); + verify(client, never()).executeDdl(argThat(isEmptyListOfStrings()), isNull()); ParsedStatement statement = mock(ParsedStatement.class); when(statement.getType()).thenReturn(StatementType.DDL); @@ -331,14 +336,14 @@ public void testRunBatch() { batch = createSubject(client); batch.executeDdlAsync(CallType.SYNC, statement); get(batch.runBatchAsync(CallType.SYNC)); - verify(client).executeDdl(argThat(isListOfStringsWithSize(1))); + verify(client).executeDdl(argThat(isListOfStringsWithSize(1)), isNull()); client = createDefaultMockDdlClient(); batch = createSubject(client); batch.executeDdlAsync(CallType.SYNC, statement); batch.executeDdlAsync(CallType.SYNC, statement); get(batch.runBatchAsync(CallType.SYNC)); - verify(client).executeDdl(argThat(isListOfStringsWithSize(2))); + verify(client).executeDdl(argThat(isListOfStringsWithSize(2)), isNull()); assertThat(batch.getState(), is(UnitOfWorkState.RAN)); boolean exception = false; try { @@ -384,7 +389,46 @@ public void testRunBatch() { } assertThat(exception, is(true)); assertThat(batch.getState(), is(UnitOfWorkState.RUN_FAILED)); - verify(client).executeDdl(argThat(isListOfStringsWithSize(2))); + verify(client).executeDdl(argThat(isListOfStringsWithSize(2)), isNull()); + + // verify when protoDescriptors is null + client = createDefaultMockDdlClient(); + batch = + DdlBatch.newBuilder() + .setDdlClient(client) + .setDatabaseClient(mock(DatabaseClient.class)) + .withStatementExecutor(new StatementExecutor()) + .setProtoDescriptors(null) + .build(); + batch.executeDdlAsync(CallType.SYNC, statement); + batch.executeDdlAsync(CallType.SYNC, statement); + get(batch.runBatchAsync(CallType.SYNC)); + verify(client).executeDdl(argThat(isListOfStringsWithSize(2)), isNull()); + + // verify when protoDescriptors is not null + byte[] protoDescriptors; + try { + InputStream in = + DdlBatchTest.class + .getClassLoader() + .getResourceAsStream("com/google/cloud/spanner/descriptors.pb"); + assertNotNull(in); + protoDescriptors = ByteStreams.toByteArray(in); + } catch (Exception e) { + throw SpannerExceptionFactory.newSpannerException(e); + } + client = createDefaultMockDdlClient(); + batch = + DdlBatch.newBuilder() + .setDdlClient(client) + .setDatabaseClient(mock(DatabaseClient.class)) + .withStatementExecutor(new StatementExecutor()) + .setProtoDescriptors(protoDescriptors) + .build(); + batch.executeDdlAsync(CallType.SYNC, statement); + batch.executeDdlAsync(CallType.SYNC, statement); + get(batch.runBatchAsync(CallType.SYNC)); + verify(client).executeDdl(argThat(isListOfStringsWithSize(2)), any(byte[].class)); } @Test @@ -403,7 +447,8 @@ public void testUpdateCount() throws InterruptedException, ExecutionException { OperationFuture operationFuture = mock(OperationFuture.class); when(operationFuture.get()).thenReturn(null); when(operationFuture.getMetadata()).thenReturn(metadataFuture); - when(client.executeDdl(argThat(isListOfStringsWithSize(2)))).thenReturn(operationFuture); + when(client.executeDdl(argThat(isListOfStringsWithSize(2)), isNull())) + .thenReturn(operationFuture); DdlBatch batch = DdlBatch.newBuilder() .withStatementExecutor(new StatementExecutor()) @@ -441,7 +486,8 @@ public void testFailedUpdateCount() throws InterruptedException, ExecutionExcept new ExecutionException( "ddl statement failed", Status.INVALID_ARGUMENT.asRuntimeException())); when(operationFuture.getMetadata()).thenReturn(metadataFuture); - when(client.executeDdl(argThat(isListOfStringsWithSize(2)))).thenReturn(operationFuture); + when(client.executeDdl(argThat(isListOfStringsWithSize(2)), isNull())) + .thenReturn(operationFuture); DdlBatch batch = DdlBatch.newBuilder() .withStatementExecutor(new StatementExecutor()) @@ -483,7 +529,8 @@ public void testFailedAfterFirstStatement() throws InterruptedException, Executi new ExecutionException( "ddl statement failed", Status.INVALID_ARGUMENT.asRuntimeException())); when(operationFuture.getMetadata()).thenReturn(metadataFuture); - when(client.executeDdl(argThat(isListOfStringsWithSize(2)))).thenReturn(operationFuture); + when(client.executeDdl(argThat(isListOfStringsWithSize(2)), isNull())) + .thenReturn(operationFuture); DdlBatch batch = DdlBatch.newBuilder() .withStatementExecutor(new StatementExecutor()) @@ -514,8 +561,8 @@ public void testAbort() { DdlBatch batch = createSubject(client); batch.abortBatch(); assertThat(batch.getState(), is(UnitOfWorkState.ABORTED)); - verify(client, never()).executeDdl(anyString()); - verify(client, never()).executeDdl(anyList()); + verify(client, never()).executeDdl(anyString(), isNull()); + verify(client, never()).executeDdl(anyList(), isNull()); ParsedStatement statement = mock(ParsedStatement.class); when(statement.getType()).thenReturn(StatementType.DDL); @@ -526,21 +573,21 @@ public void testAbort() { batch = createSubject(client); batch.executeDdlAsync(CallType.SYNC, statement); batch.abortBatch(); - verify(client, never()).executeDdl(anyList()); + verify(client, never()).executeDdl(anyList(), isNull()); client = createDefaultMockDdlClient(); batch = createSubject(client); batch.executeDdlAsync(CallType.SYNC, statement); batch.executeDdlAsync(CallType.SYNC, statement); batch.abortBatch(); - verify(client, never()).executeDdl(anyList()); + verify(client, never()).executeDdl(anyList(), isNull()); client = createDefaultMockDdlClient(); batch = createSubject(client); batch.executeDdlAsync(CallType.SYNC, statement); batch.executeDdlAsync(CallType.SYNC, statement); batch.abortBatch(); - verify(client, never()).executeDdl(anyList()); + verify(client, never()).executeDdl(anyList(), isNull()); boolean exception = false; try { get(batch.runBatchAsync(CallType.SYNC)); @@ -551,7 +598,7 @@ public void testAbort() { exception = true; } assertThat(exception, is(true)); - verify(client, never()).executeDdl(anyList()); + verify(client, never()).executeDdl(anyList(), isNull()); } @Test 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 d46e4dca592..c61635fce23 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 @@ -17,17 +17,25 @@ package com.google.cloud.spanner.connection; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.common.io.ByteStreams; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -39,11 +47,13 @@ @RunWith(JUnit4.class) public class DdlClientTests { + private final String projectId = "test-project"; private final String instanceId = "test-instance"; private final String databaseId = "test-database"; private DdlClient createSubject(DatabaseAdminClient client) { return DdlClient.newBuilder() + .setProjectId(projectId) .setInstanceId(instanceId) .setDatabaseName(databaseId) .setDatabaseAdminClient(client) @@ -52,21 +62,48 @@ private DdlClient createSubject(DatabaseAdminClient client) { @Test public void testExecuteDdl() throws InterruptedException, ExecutionException { + byte[] protoDescriptors; + try { + InputStream in = + DdlBatchTest.class + .getClassLoader() + .getResourceAsStream("com/google/cloud/spanner/descriptors.pb"); + assertNotNull(in); + protoDescriptors = ByteStreams.toByteArray(in); + } catch (Exception e) { + throw SpannerExceptionFactory.newSpannerException(e); + } + DatabaseAdminClient client = mock(DatabaseAdminClient.class); + Database database = mock(Database.class); + Database.Builder databaseBuilder = mock(Database.Builder.class); @SuppressWarnings("unchecked") OperationFuture operation = mock(OperationFuture.class); + when(operation.get()).thenReturn(null); - when(client.updateDatabaseDdl(eq(instanceId), eq(databaseId), anyList(), isNull())) - .thenReturn(operation); + when(client.newDatabaseBuilder((DatabaseId.of(projectId, instanceId, databaseId)))) + .thenReturn(databaseBuilder); + when(databaseBuilder.setProtoDescriptors(protoDescriptors)).thenReturn(databaseBuilder); + when(databaseBuilder.build()).thenReturn(database); + when(client.updateDatabaseDdl(eq(database), anyList(), isNull())).thenReturn(operation); + DdlClient subject = createSubject(client); String ddl = "CREATE TABLE FOO"; - subject.executeDdl(ddl); - verify(client).updateDatabaseDdl(instanceId, databaseId, Collections.singletonList(ddl), null); + subject.executeDdl(ddl, null); + verify(databaseBuilder, never()).setProtoDescriptors(any(byte[].class)); + verify(client).updateDatabaseDdl(database, Collections.singletonList(ddl), null); subject = createSubject(client); List ddlList = Arrays.asList("CREATE TABLE FOO", "DROP TABLE FOO"); - subject.executeDdl(ddlList); - verify(client).updateDatabaseDdl(instanceId, databaseId, ddlList, null); + subject.executeDdl(ddlList, null); + verify(databaseBuilder, never()).setProtoDescriptors(any(byte[].class)); + verify(client).updateDatabaseDdl(database, ddlList, null); + + subject = createSubject(client); + ddlList = Arrays.asList("CREATE PROTO BUNDLE", "CREATE TABLE FOO"); + subject.executeDdl(ddlList, protoDescriptors); + verify(databaseBuilder).setProtoDescriptors(protoDescriptors); + verify(client).updateDatabaseDdl(database, ddlList, null); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ProtoDescriptorsConverterTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ProtoDescriptorsConverterTest.java new file mode 100644 index 00000000000..b2041d1a332 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ProtoDescriptorsConverterTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.connection; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.connection.ClientSideStatementImpl.CompileException; +import com.google.cloud.spanner.connection.ClientSideStatementValueConverters.ProtoDescriptorsConverter; +import com.google.common.io.ByteStreams; +import java.io.InputStream; +import java.util.Base64; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ProtoDescriptorsConverterTest { + @Test + public void testConvert() throws CompileException { + String allowedValues = + ReadOnlyStalenessConverterTest.getAllowedValues( + ProtoDescriptorsConverter.class, Dialect.GOOGLE_STANDARD_SQL); + assertNotNull(allowedValues); + ProtoDescriptorsConverter converter = new ProtoDescriptorsConverter(allowedValues); + + byte[] protoDescriptors; + try { + InputStream in = + ProtoDescriptorsConverterTest.class + .getClassLoader() + .getResourceAsStream("com/google/cloud/spanner/descriptors.pb"); + assertNotNull(in); + protoDescriptors = ByteStreams.toByteArray(in); + } catch (Exception e) { + throw SpannerExceptionFactory.newSpannerException(e); + } + + assertNull(converter.convert("")); + assertNull(converter.convert("null")); + assertNull(converter.convert(null)); + assertNull(converter.convert("random string")); + + assertArrayEquals( + converter.convert(Base64.getEncoder().encodeToString(protoDescriptors)), protoDescriptors); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ProtoDescriptorsFileConverterTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ProtoDescriptorsFileConverterTest.java new file mode 100644 index 00000000000..67428327057 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ProtoDescriptorsFileConverterTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.connection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.connection.ClientSideStatementImpl.CompileException; +import com.google.cloud.spanner.connection.ClientSideStatementValueConverters.ProtoDescriptorsFileConverter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ProtoDescriptorsFileConverterTest { + @Test + public void testConvert() throws CompileException { + String allowedValues = + ReadOnlyStalenessConverterTest.getAllowedValues( + ProtoDescriptorsFileConverter.class, Dialect.GOOGLE_STANDARD_SQL); + assertNotNull(allowedValues); + ProtoDescriptorsFileConverter converter = new ProtoDescriptorsFileConverter(allowedValues); + + assertNull(converter.convert("")); + assertNull(converter.convert(null)); + + String filePath = "com/google/cloud/spanner/descriptors.pb"; + assertEquals(converter.convert(filePath), filePath); + } +} 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 a96fc59d10f..9bbc613d7cf 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 @@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; @@ -56,8 +57,10 @@ import com.google.cloud.spanner.connection.StatementExecutor.StatementTimeout; import com.google.cloud.spanner.connection.UnitOfWork.CallType; import com.google.common.base.Preconditions; +import com.google.common.io.ByteStreams; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import com.google.spanner.v1.ResultSetStats; +import java.io.InputStream; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; @@ -299,8 +302,8 @@ private DdlClient createDefaultMockDdlClient() { final OperationFuture operation = mock(OperationFuture.class); when(operation.get()).thenReturn(null); - when(ddlClient.executeDdl(anyString())).thenCallRealMethod(); - when(ddlClient.executeDdl(anyList())).thenReturn(operation); + when(ddlClient.executeDdl(anyString(), any())).thenCallRealMethod(); + when(ddlClient.executeDdl(anyList(), any())).thenReturn(operation); return ddlClient; } catch (Exception e) { throw new RuntimeException(e); @@ -314,7 +317,8 @@ private SingleUseTransaction createSubject() { TimestampBound.strong(), AutocommitDmlMode.TRANSACTIONAL, CommitBehavior.SUCCEED, - 0L); + 0L, + null); } private SingleUseTransaction createSubject(AutocommitDmlMode dmlMode) { @@ -324,7 +328,8 @@ private SingleUseTransaction createSubject(AutocommitDmlMode dmlMode) { TimestampBound.strong(), dmlMode, CommitBehavior.SUCCEED, - 0L); + 0L, + null); } private SingleUseTransaction createSubject(CommitBehavior commitBehavior) { @@ -334,7 +339,8 @@ private SingleUseTransaction createSubject(CommitBehavior commitBehavior) { TimestampBound.strong(), AutocommitDmlMode.TRANSACTIONAL, commitBehavior, - 0L); + 0L, + null); } private SingleUseTransaction createDdlSubject(DdlClient ddlClient) { @@ -344,7 +350,20 @@ private SingleUseTransaction createDdlSubject(DdlClient ddlClient) { TimestampBound.strong(), AutocommitDmlMode.TRANSACTIONAL, CommitBehavior.SUCCEED, - 0L); + 0L, + null); + } + + private SingleUseTransaction createProtoDescriptorsSubject( + DdlClient ddlClient, byte[] protoDescriptors) { + return createSubject( + ddlClient, + false, + TimestampBound.strong(), + AutocommitDmlMode.TRANSACTIONAL, + CommitBehavior.SUCCEED, + 0L, + protoDescriptors); } private SingleUseTransaction createReadOnlySubject(TimestampBound staleness) { @@ -354,7 +373,8 @@ private SingleUseTransaction createReadOnlySubject(TimestampBound staleness) { staleness, AutocommitDmlMode.TRANSACTIONAL, CommitBehavior.SUCCEED, - 0L); + 0L, + null); } private SingleUseTransaction createSubject( @@ -363,7 +383,8 @@ private SingleUseTransaction createSubject( TimestampBound staleness, AutocommitDmlMode dmlMode, final CommitBehavior commitBehavior, - long timeout) { + long timeout, + byte[] protoDescriptors) { DatabaseClient dbClient = mock(DatabaseClient.class); com.google.cloud.spanner.ReadOnlyTransaction singleUse = new SimpleReadOnlyTransaction(staleness); @@ -450,6 +471,7 @@ public TransactionRunner allowNestedTransaction() { .setStatementTimeout( timeout == 0L ? nullTimeout() : timeout(timeout, TimeUnit.MILLISECONDS)) .withStatementExecutor(executor) + .setProtoDescriptors(protoDescriptors) .build(); } @@ -537,7 +559,34 @@ public void testExecuteDdl() { DdlClient ddlClient = createDefaultMockDdlClient(); SingleUseTransaction subject = createDdlSubject(ddlClient); get(subject.executeDdlAsync(CallType.SYNC, ddl)); - verify(ddlClient).executeDdl(sql); + verify(ddlClient).executeDdl(sql, null); + } + + @Test + public void testExecuteDdlWithProtoDescriptors() { + String sql = "CREATE TABLE FOO"; + ParsedStatement ddl = createParsedDdl(sql); + DdlClient ddlClient = createDefaultMockDdlClient(); + // verify when protoDescriptors value is null + SingleUseTransaction subject = createProtoDescriptorsSubject(ddlClient, null); + get(subject.executeDdlAsync(CallType.SYNC, ddl)); + verify(ddlClient).executeDdl(sql, null); + + // verify when protoDescriptors value is not null + byte[] protoDescriptors; + try { + InputStream in = + SingleUseTransactionTest.class + .getClassLoader() + .getResourceAsStream("com/google/cloud/spanner/descriptors.pb"); + assertNotNull(in); + protoDescriptors = ByteStreams.toByteArray(in); + } catch (Exception e) { + throw SpannerExceptionFactory.newSpannerException(e); + } + subject = createProtoDescriptorsSubject(ddlClient, protoDescriptors); + get(subject.executeDdlAsync(CallType.SYNC, ddl)); + verify(ddlClient).executeDdl(sql, protoDescriptors); } @Test @@ -729,7 +778,7 @@ public void testMultiUse() { DdlClient ddlClient = createDefaultMockDdlClient(); SingleUseTransaction subject = createDdlSubject(ddlClient); get(subject.executeDdlAsync(CallType.SYNC, ddl)); - verify(ddlClient).executeDdl(sql); + verify(ddlClient).executeDdl(sql, null); try { get(subject.executeDdlAsync(CallType.SYNC, ddl)); fail("missing expected exception"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementResultImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementResultImplTest.java index c28d3b75b95..0a55ff2c420 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementResultImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementResultImplTest.java @@ -21,9 +21,13 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; +import com.google.cloud.ByteArray; import com.google.cloud.Timestamp; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ResultSet; @@ -154,6 +158,18 @@ public void testStringResultSetGetResultSet() { assertThat(subject.getResultSet().next(), is(true)); assertThat(subject.getResultSet().getString("foo"), is(equalTo("bar"))); assertThat(subject.getResultSet().next(), is(false)); + + subject = + StatementResultImpl.resultSet( + "path", "descriptors.pb", ClientSideStatementType.SHOW_PROTO_DESCRIPTORS_FILE_PATH); + assertThat(subject.getResultType(), is(equalTo(ResultType.RESULT_SET))); + assertThat( + subject.getClientSideStatementType(), + is(equalTo(ClientSideStatementType.SHOW_PROTO_DESCRIPTORS_FILE_PATH))); + assertThat(subject.getResultSet(), is(notNullValue())); + assertThat(subject.getResultSet().next(), is(true)); + assertThat(subject.getResultSet().getString("path"), is(equalTo("descriptors.pb"))); + assertThat(subject.getResultSet().next(), is(false)); } @Test @@ -190,4 +206,19 @@ public void testTimestampResultSetGetResultSet() { is(equalTo(Timestamp.ofTimeSecondsAndNanos(10L, 10)))); assertThat(subject.getResultSet().next(), is(false)); } + + @Test + public void testBytesResultSetGetResultSet() { + StatementResult subject = + StatementResultImpl.resultSet( + "foo", "protoDescriptors".getBytes(), ClientSideStatementType.SHOW_PROTO_DESCRIPTORS); + assertEquals(subject.getResultType(), ResultType.RESULT_SET); + assertEquals( + subject.getClientSideStatementType(), ClientSideStatementType.SHOW_PROTO_DESCRIPTORS); + assertNotNull(subject.getResultSet()); + assertTrue(subject.getResultSet().next()); + assertEquals( + subject.getResultSet().getBytes("foo"), ByteArray.copyFrom("protoDescriptors".getBytes())); + assertFalse(subject.getResultSet().next()); + } } diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql index 914244a02a8..b18a41b3202 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ClientSideStatementsTest.sql @@ -17701,3 +17701,797 @@ set delay_transaction_start_until_first_write = false/-; NEW_CONNECTION; @EXPECT EXCEPTION INVALID_ARGUMENT set delay_transaction_start_until_first_write =/-false; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +SET PROTO_DESCRIPTORS='PROTODESCRIPTORSBASE64'; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; + set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; + set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; + + + +set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64' ; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64' ; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64' + +; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +set +proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64' bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set%proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set_proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set&proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set$proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set@proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set!proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set*proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set(proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set)proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set+proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-#proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set\proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set?proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-/proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/#proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-set proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors='protodescriptorsbase64'/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/-proto_descriptors='protodescriptorsbase64'; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +SET PROTO_DESCRIPTORS_FILE_PATH='SRC/TEST/RESOURCES/COM/GOOGLE/CLOUD/SPANNER/DESCRIPTORS.PB'; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; + set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; + set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; + + + +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb' ; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb' ; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb' + +; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +set +proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb' bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'%; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set%proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'_; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set_proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'&; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set&proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'$; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set$proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'@; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set@proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'!; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set!proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'*; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set*proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'(; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set(proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'); +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set)proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'+; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set+proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'-#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-#proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'\; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set\proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'?; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set?proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'-/; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set-/proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'/#; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/#proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'/-; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +set/-proto_descriptors_file_path='src/test/resources/com/google/cloud/spanner/descriptors.pb'; +NEW_CONNECTION; +show variable proto_descriptors; +NEW_CONNECTION; +SHOW VARIABLE PROTO_DESCRIPTORS; +NEW_CONNECTION; +show variable proto_descriptors; +NEW_CONNECTION; + show variable proto_descriptors; +NEW_CONNECTION; + show variable proto_descriptors; +NEW_CONNECTION; + + + +show variable proto_descriptors; +NEW_CONNECTION; +show variable proto_descriptors ; +NEW_CONNECTION; +show variable proto_descriptors ; +NEW_CONNECTION; +show variable proto_descriptors + +; +NEW_CONNECTION; +show variable proto_descriptors; +NEW_CONNECTION; +show variable proto_descriptors; +NEW_CONNECTION; +show +variable +proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors%; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable%proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable_proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors&; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable&proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors$; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable$proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors@; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable@proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors!; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable!proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors*; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable*proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors(; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable(proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors); +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable)proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors+; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable+proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors-#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-#proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors\; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable\proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors?; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable?proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors-/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-/proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors/#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/#proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-show variable proto_descriptors; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors/-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/-proto_descriptors; +NEW_CONNECTION; +show variable proto_descriptors_file_path; +NEW_CONNECTION; +SHOW VARIABLE PROTO_DESCRIPTORS_FILE_PATH; +NEW_CONNECTION; +show variable proto_descriptors_file_path; +NEW_CONNECTION; + show variable proto_descriptors_file_path; +NEW_CONNECTION; + show variable proto_descriptors_file_path; +NEW_CONNECTION; + + + +show variable proto_descriptors_file_path; +NEW_CONNECTION; +show variable proto_descriptors_file_path ; +NEW_CONNECTION; +show variable proto_descriptors_file_path ; +NEW_CONNECTION; +show variable proto_descriptors_file_path + +; +NEW_CONNECTION; +show variable proto_descriptors_file_path; +NEW_CONNECTION; +show variable proto_descriptors_file_path; +NEW_CONNECTION; +show +variable +proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +foo show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path bar; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +%show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path%; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable%proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +_show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path_; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable_proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +&show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path&; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable&proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +$show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path$; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable$proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +@show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path@; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable@proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +!show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path!; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable!proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +*show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path*; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable*proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +(show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path(; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable(proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +)show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path); +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable)proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT ++show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path+; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable+proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-#show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path-#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-#proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +\show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path\; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable\proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +?show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path?; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable?proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +-/show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path-/; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable-/proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/#show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path/#; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/#proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION INVALID_ARGUMENT +/-show variable proto_descriptors_file_path; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable proto_descriptors_file_path/-; +NEW_CONNECTION; +@EXPECT EXCEPTION UNIMPLEMENTED +show variable/-proto_descriptors_file_path; diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITDdlTest.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITDdlTest.sql index 2dea0423151..8d81cef0d0f 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITDdlTest.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITDdlTest.sql @@ -187,3 +187,44 @@ RUN BATCH; START BATCH DDL; ABORT BATCH; + +NEW_CONNECTION; +-- Set proto descriptors using relative path to the descriptors.pb file. This gets applied for next DDL statement +SET PROTO_DESCRIPTORS_FILE_PATH = 'src/test/resources/com/google/cloud/spanner/descriptors.pb'; +-- Check if Proto descriptors is set +@EXPECT RESULT_SET 'PROTO_DESCRIPTORS_FILE_PATH' +SHOW VARIABLE PROTO_DESCRIPTORS_FILE_PATH; + +CREATE PROTO BUNDLE (spanner.examples.music.Genre); +-- Check if Proto descriptors is reset to null +@EXPECT RESULT_SET 'PROTO_DESCRIPTORS',null +SHOW VARIABLE PROTO_DESCRIPTORS; +@EXPECT RESULT_SET 'PROTO_DESCRIPTORS_FILE_PATH',null +SHOW VARIABLE PROTO_DESCRIPTORS_FILE_PATH; + +-- Set Proto Descriptor as base64 string. This gets applied to all statements in next DDL batch +SET PROTO_DESCRIPTORS = 'CvgBCgxzaW5nZXIucHJvdG8SFnNwYW5uZXIuZXhhbXBsZXMubXVzaWMinwEKClNpbmdlckluZm8SGwoJc2luZ2VyX2lkGAEgASgDUghzaW5nZXJJZBIdCgpiaXJ0aF9kYXRlGAIgASgJUgliaXJ0aERhdGUSIAoLbmF0aW9uYWxpdHkYAyABKAlSC25hdGlvbmFsaXR5EjMKBWdlbnJlGAQgASgOMh0uc3Bhbm5lci5leGFtcGxlcy5tdXNpYy5HZW5yZVIFZ2VucmUqLgoFR2VucmUSBwoDUE9QEAASCAoESkFaWhABEggKBEZPTEsQAhIICgRST0NLEAM='; + +@EXPECT RESULT_SET 'PROTO_DESCRIPTORS' +SHOW VARIABLE PROTO_DESCRIPTORS; + +START BATCH DDL; +ALTER PROTO BUNDLE INSERT (spanner.examples.music.SingerInfo); +CREATE TABLE Singers ( + SingerId INT64 NOT NULL, + FirstName STRING(1024), + LastName STRING(1024), + SingerInfo spanner.examples.music.SingerInfo, + SingerGenre spanner.examples.music.Genre +) PRIMARY KEY (SingerId); +-- Run the batch +RUN BATCH; + +-- Check if Proto descriptors is reset to null +@EXPECT RESULT_SET 'PROTO_DESCRIPTORS',null +SHOW VARIABLE PROTO_DESCRIPTORS; +-- Check that the table is created +@EXPECT RESULT_SET +SELECT COUNT(*) AS ACTUAL, 1 AS EXPECTED +FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_NAME='Singers';