diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatement.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatement.java index 82c1084afd7e..05e4e96f5fda 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatement.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatement.java @@ -48,20 +48,28 @@ * paramName, Type value) for the appropriate type. For example: * *
{@code
- * BoundStatementt boundStatement = preparedStatement.bind()
+ * BoundStatement boundStatement = preparedStatement.bind()
  *     .setBytesParam("qualifier", ByteString.copyFromUtf8("test"))
  *     .setBytesParam("key", ByteString.copyFromUtf8("testKey"))
  *     .build();
  * }
+ * + *

View parameters can also be specified on the statement using {@link + * Builder#setStringViewParameter(String, String)} or {@link Builder#setStringViewParameters(Map)}. */ public class BoundStatement { private final PreparedStatementImpl preparedStatement; private final Map params; + private final Map viewParameters; - private BoundStatement(PreparedStatementImpl preparedStatement, Map params) { + private BoundStatement( + PreparedStatementImpl preparedStatement, + Map params, + Map viewParameters) { this.preparedStatement = preparedStatement; this.params = params; + this.viewParameters = viewParameters; } /** @@ -78,6 +86,7 @@ public static class Builder { private final PreparedStatementImpl preparedStatement; private final Map> paramTypes; private final Map params; + private final Map viewParameters; /** * Creates a builder from a {@link PreparedStatement} @@ -90,6 +99,7 @@ public Builder(PreparedStatementImpl preparedStatement, Map> this.preparedStatement = preparedStatement; this.paramTypes = paramTypes; this.params = new HashMap<>(); + this.viewParameters = new HashMap<>(); } /** Builds a {@link BoundStatement} from the builder */ @@ -101,7 +111,27 @@ public BoundStatement build() { "Attempting to build BoundStatement without binding parameter: " + paramName); } } - return new BoundStatement(preparedStatement, ImmutableMap.copyOf(params)); + return new BoundStatement( + preparedStatement, ImmutableMap.copyOf(params), ImmutableMap.copyOf(viewParameters)); + } + + /** + * Sets a view parameter with the name {@code paramName} and the String typed value {@code + * value}. + */ + public Builder setStringViewParameter(String paramName, @Nullable String value) { + Preconditions.checkNotNull(paramName, "paramName cannot be null"); + viewParameters.put(paramName, stringParamOf(value)); + return this; + } + + /** Sets String typed view parameters from a map. */ + public Builder setStringViewParameters(Map viewParameters) { + Preconditions.checkNotNull(viewParameters, "viewParameters cannot be null"); + for (Map.Entry entry : viewParameters.entrySet()) { + setStringViewParameter(entry.getKey(), entry.getValue()); + } + return this; } /** @@ -372,7 +402,8 @@ public ExecuteQueryRequest toProto( requestContext.getProjectId(), requestContext.getInstanceId())) .setAppProfileId(requestContext.getAppProfileId()) .setPreparedQuery(preparedQuery) - .putAllParams(params); + .putAllParams(params) + .putAllViewParameters(viewParameters); if (resumeToken != null) { requestBuilder.setResumeToken(resumeToken); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatementTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatementTest.java index 71f46610a203..51e1cd591ac5 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatementTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/sql/BoundStatementTest.java @@ -869,4 +869,73 @@ public void builderValidatesAllParamsAreSet() { assertThat(e.getMessage()) .contains("Attempting to build BoundStatement without binding parameter: bytesParam"); } + + @Test + public void statementWithStringViewParameters() { + Value stringVal = Value.newBuilder().setType(stringType()).setStringValue("alice").build(); + Value locationVal = Value.newBuilder().setType(stringType()).setStringValue("us-east1").build(); + + BoundStatement s = + boundStatementBuilder() + .setStringViewParameter("user_id", "alice") + .setStringViewParameter("location", "us-east1") + .build(); + + assertThat(s.toProto(EXPECTED_PREPARED_QUERY, REQUEST_CONTEXT, NO_RESUME_TOKEN)) + .isEqualTo( + ExecuteQueryRequest.newBuilder() + .setPreparedQuery(EXPECTED_PREPARED_QUERY) + .setInstanceName(EXPECTED_INSTANCE_NAME) + .setAppProfileId(EXPECTED_APP_PROFILE) + .putViewParameters("user_id", stringVal) + .putViewParameters("location", locationVal) + .build()); + } + + @Test + public void statementWithStringViewParametersMap() { + Value stringVal = Value.newBuilder().setType(stringType()).setStringValue("alice").build(); + Value locationVal = Value.newBuilder().setType(stringType()).setStringValue("us-east1").build(); + + HashMap viewParams = new HashMap<>(); + viewParams.put("user_id", "alice"); + viewParams.put("location", "us-east1"); + + BoundStatement s = boundStatementBuilder().setStringViewParameters(viewParams).build(); + + assertThat(s.toProto(EXPECTED_PREPARED_QUERY, REQUEST_CONTEXT, NO_RESUME_TOKEN)) + .isEqualTo( + ExecuteQueryRequest.newBuilder() + .setPreparedQuery(EXPECTED_PREPARED_QUERY) + .setInstanceName(EXPECTED_INSTANCE_NAME) + .setAppProfileId(EXPECTED_APP_PROFILE) + .putViewParameters("user_id", stringVal) + .putViewParameters("location", locationVal) + .build()); + } + + @Test + public void statementWithNullStringViewParameter() { + Value nullStringVal = Value.newBuilder().setType(stringType()).build(); + + BoundStatement s = + boundStatementBuilder().setStringViewParameter("user_id", (String) null).build(); + + assertThat(s.toProto(EXPECTED_PREPARED_QUERY, REQUEST_CONTEXT, NO_RESUME_TOKEN)) + .isEqualTo( + ExecuteQueryRequest.newBuilder() + .setPreparedQuery(EXPECTED_PREPARED_QUERY) + .setInstanceName(EXPECTED_INSTANCE_NAME) + .setAppProfileId(EXPECTED_APP_PROFILE) + .putViewParameters("user_id", nullStringVal) + .build()); + } + + @Test + public void setStringViewParametersNullChecks() { + BoundStatement.Builder builder = boundStatementBuilder(); + + assertThrows(NullPointerException.class, () -> builder.setStringViewParameter(null, "alice")); + assertThrows(NullPointerException.class, () -> builder.setStringViewParameters(null)); + } } diff --git a/java-bigtable/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/BoundStatementDeserializer.java b/java-bigtable/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/BoundStatementDeserializer.java index 43da147274f7..76513fa0bd8f 100644 --- a/java-bigtable/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/BoundStatementDeserializer.java +++ b/java-bigtable/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/BoundStatementDeserializer.java @@ -128,6 +128,17 @@ static BoundStatement toBoundStatement( throw new IllegalArgumentException("Unexpected query param type in param: " + value); } } + for (Map.Entry entry : request.getRequest().getViewParametersMap().entrySet()) { + String name = entry.getKey(); + Value value = entry.getValue(); + if (value.getKindCase().equals(KindCase.KIND_NOT_SET)) { + boundStatementBuilder.setStringViewParameter(name, null); + } else if (value.getKindCase().equals(KindCase.STRING_VALUE)) { + boundStatementBuilder.setStringViewParameter(name, value.getStringValue()); + } else { + throw new IllegalArgumentException("Unexpected view parameter type in param: " + value); + } + } return boundStatementBuilder.build(); }