This is an automated email from the ASF dual-hosted git repository.

upthewaterspout pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new b730a43  GEODE-3126: Adding a query message to the protobuf protocol
b730a43 is described below

commit b730a430b50b5d847ae8c029a5c7ac12cbc315b7
Author: Dan Smith <[email protected]>
AuthorDate: Wed Feb 21 17:16:00 2018 -0800

    GEODE-3126: Adding a query message to the protobuf protocol
    
    Adding an oql query request and response to the new protocol.
---
 .../src/main/proto/v1/basicTypes.proto             |  12 ++
 .../src/main/proto/v1/clientProtocol.proto         |   3 +
 .../src/main/proto/v1/region_API.proto             |  16 ++
 .../OqlQueryRequestOperationHandler.java           | 126 ++++++++++++++++
 .../registry/ProtobufOperationContextRegistry.java |   9 ++
 .../serialization/exception/DecodingException.java |   2 +-
 .../serialization/exception/EncodingException.java |   2 +-
 ...ueryRequestOperationHandlerIntegrationTest.java | 164 +++++++++++++++++++++
 .../OqlQueryRequestOperationHandlerJUnitTest.java  | 138 +++++++++++++++++
 9 files changed, 470 insertions(+), 2 deletions(-)

diff --git a/geode-protobuf-messages/src/main/proto/v1/basicTypes.proto 
b/geode-protobuf-messages/src/main/proto/v1/basicTypes.proto
index a0ca4aa..2b306dd 100644
--- a/geode-protobuf-messages/src/main/proto/v1/basicTypes.proto
+++ b/geode-protobuf-messages/src/main/proto/v1/basicTypes.proto
@@ -29,6 +29,18 @@ message Entry {
     EncodedValue value = 2;
 }
 
+message EncodedValueList {
+  repeated EncodedValue element = 1;
+}
+
+//Represents a table, such as the results of
+//an OQL query. The field names and all rows
+//should have the same number of elements.
+message Table {
+    repeated string fieldName = 1;
+    repeated EncodedValueList row = 2;
+}
+
 message EncodedValue {
     oneof value{
         int32 intResult = 1;
diff --git a/geode-protobuf-messages/src/main/proto/v1/clientProtocol.proto 
b/geode-protobuf-messages/src/main/proto/v1/clientProtocol.proto
index e4181c7..d049aec 100644
--- a/geode-protobuf-messages/src/main/proto/v1/clientProtocol.proto
+++ b/geode-protobuf-messages/src/main/proto/v1/clientProtocol.proto
@@ -66,6 +66,9 @@ message Message {
 
         ExecuteFunctionOnGroupRequest executeFunctionOnGroupRequest = 24;
         ExecuteFunctionOnGroupResponse executeFunctionOnGroupResponse= 25;
+
+        OQLQueryRequest oqlQueryRequest = 26;
+        OQLQueryResponse oqlQueryResponse = 27;
     }
 }
 
diff --git a/geode-protobuf-messages/src/main/proto/v1/region_API.proto 
b/geode-protobuf-messages/src/main/proto/v1/region_API.proto
index b68eb5d..c3465db 100644
--- a/geode-protobuf-messages/src/main/proto/v1/region_API.proto
+++ b/geode-protobuf-messages/src/main/proto/v1/region_API.proto
@@ -84,3 +84,19 @@ message GetRegionRequest {
 message GetRegionResponse {
     Region region = 1;
 }
+
+message OQLQueryRequest {
+    string query = 1;
+    repeated EncodedValue bindParameter = 2;
+}
+
+//Response to an OQL query. Depending on the query,
+//it may return a single value, a list of values, or a table
+//of values.
+message OQLQueryResponse {
+    oneof result {
+      EncodedValue singleResult = 1;
+      EncodedValueList listResult = 2;
+      Table tableResult = 3;
+    }
+}
diff --git 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandler.java
 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandler.java
new file mode 100644
index 0000000..448fcea
--- /dev/null
+++ 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandler.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.protocol.protobuf.v1.operations;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.cache.query.Query;
+import org.apache.geode.cache.query.QueryException;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.cache.query.Struct;
+import org.apache.geode.cache.query.internal.InternalQueryService;
+import org.apache.geode.cache.query.types.StructType;
+import org.apache.geode.internal.exception.InvalidExecutionContextException;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.protocol.operations.ProtobufOperationHandler;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes.EncodedValue;
+import org.apache.geode.internal.protocol.protobuf.v1.Failure;
+import org.apache.geode.internal.protocol.protobuf.v1.MessageExecutionContext;
+import 
org.apache.geode.internal.protocol.protobuf.v1.ProtobufSerializationService;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryRequest;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryResponse;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryResponse.Builder;
+import org.apache.geode.internal.protocol.protobuf.v1.Result;
+import org.apache.geode.internal.protocol.protobuf.v1.Success;
+import 
org.apache.geode.internal.protocol.protobuf.v1.serialization.exception.DecodingException;
+import 
org.apache.geode.internal.protocol.protobuf.v1.serialization.exception.EncodingException;
+import 
org.apache.geode.internal.protocol.protobuf.v1.state.exception.ConnectionStateException;
+
+public class OqlQueryRequestOperationHandler
+    implements ProtobufOperationHandler<OQLQueryRequest, OQLQueryResponse> {
+  Logger logger = LogService.getLogger();
+
+  @Override
+  public Result<OQLQueryResponse> process(final ProtobufSerializationService 
serializationService,
+      final OQLQueryRequest request, final MessageExecutionContext 
messageExecutionContext)
+      throws InvalidExecutionContextException, ConnectionStateException, 
EncodingException,
+      DecodingException {
+    String queryString = request.getQuery();
+    List<EncodedValue> encodedParameters = request.getBindParameterList();
+
+    InternalQueryService queryService = 
messageExecutionContext.getCache().getQueryService();
+
+    Query query = queryService.newQuery(queryString);
+
+    Object[] bindParameters = decodeBindParameters(serializationService, 
encodedParameters);
+    try {
+      Object results = query.execute(bindParameters);
+      return Success.of(encodeResults(serializationService, results));
+    } catch (QueryException e) {
+      logger.info("Query failed: " + query, e);
+      return Failure.of(e);
+    }
+
+  }
+
+  private Object[] decodeBindParameters(final ProtobufSerializationService 
serializationService,
+      final List<EncodedValue> encodedParameters) {
+    Object[] bindParameters = new Object[encodedParameters.size()];
+    for (int i = 0; i < encodedParameters.size(); i++) {
+      bindParameters[i] = 
serializationService.decode(encodedParameters.get(i));
+    }
+    return bindParameters;
+  }
+
+  private OQLQueryResponse encodeResults(final ProtobufSerializationService 
serializationService,
+      final Object value) throws EncodingException {
+    final Builder builder = OQLQueryResponse.newBuilder();
+
+    // The result is a single value
+    if (!(value instanceof SelectResults)) {
+      builder.setSingleResult(serializationService.encode(value));
+      return builder.build();
+    }
+
+    SelectResults<?> selectResults = (SelectResults<?>) value;
+
+    // The result is a list of objects
+    if (!selectResults.getCollectionType().getElementType().isStructType()) {
+      BasicTypes.EncodedValueList.Builder listResult = 
BasicTypes.EncodedValueList.newBuilder();
+      
selectResults.stream().map(serializationService::encode).forEach(listResult::addElement);
+      builder.setListResult(listResult);
+      return builder.build();
+    }
+
+    // The result is a list of structs
+    SelectResults<Struct> structResults = (SelectResults<Struct>) 
selectResults;
+
+    StructType elementType = (StructType) 
structResults.getCollectionType().getElementType();
+    BasicTypes.Table.Builder tableResult = BasicTypes.Table.newBuilder();
+    tableResult.addAllFieldName(Arrays.asList(elementType.getFieldNames()));
+
+    for (Struct row : structResults) {
+      tableResult.addRow(encodeStruct(serializationService, row));
+    }
+    builder.setTableResult(tableResult);
+
+    return builder.build();
+  }
+
+  private BasicTypes.EncodedValueList.Builder encodeStruct(
+      final ProtobufSerializationService serializationService, final Struct 
row)
+      throws EncodingException {
+    BasicTypes.EncodedValueList.Builder structBuilder = 
BasicTypes.EncodedValueList.newBuilder();
+    for (Object element : row.getFieldValues()) {
+      structBuilder.addElement(serializationService.encode(element));
+    }
+    return structBuilder;
+  }
+
+}
diff --git 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/registry/ProtobufOperationContextRegistry.java
 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/registry/ProtobufOperationContextRegistry.java
index 8043d10..4811bac 100644
--- 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/registry/ProtobufOperationContextRegistry.java
+++ 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/registry/ProtobufOperationContextRegistry.java
@@ -20,6 +20,7 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.geode.annotations.Experimental;
 import org.apache.geode.internal.protocol.protobuf.v1.ClientProtocol;
+import 
org.apache.geode.internal.protocol.protobuf.v1.ClientProtocol.Message.MessageTypeCase;
 import org.apache.geode.internal.protocol.protobuf.v1.ProtobufOperationContext;
 import 
org.apache.geode.internal.protocol.protobuf.v1.ProtobufSerializationService;
 import org.apache.geode.internal.protocol.protobuf.v1.RegionAPI;
@@ -31,12 +32,15 @@ import 
org.apache.geode.internal.protocol.protobuf.v1.operations.GetRegionNamesR
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.GetRegionRequestOperationHandler;
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.GetRequestOperationHandler;
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.GetServerOperationHandler;
+import 
org.apache.geode.internal.protocol.protobuf.v1.operations.OqlQueryRequestOperationHandler;
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.PutAllRequestOperationHandler;
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.PutRequestOperationHandler;
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.RemoveRequestOperationHandler;
 import 
org.apache.geode.internal.protocol.protobuf.v1.operations.security.AuthenticationRequestOperationHandler;
 import org.apache.geode.management.internal.security.ResourcePermissions;
 import org.apache.geode.security.ResourcePermission;
+import org.apache.geode.security.ResourcePermission.Operation;
+import org.apache.geode.security.ResourcePermission.Resource;
 
 @Experimental
 public class ProtobufOperationContextRegistry {
@@ -143,5 +147,10 @@ public class ProtobufOperationContextRegistry {
             // Resource permissions get handled per-function, since they have 
varying permission
             // requirements.
             this::skipAuthorizationCheck));
+    operationContexts.put(MessageTypeCase.OQLQUERYREQUEST,
+        new 
ProtobufOperationContext<>(ClientProtocol.Message::getOqlQueryRequest,
+            new OqlQueryRequestOperationHandler(),
+            opsResp -> 
ClientProtocol.Message.newBuilder().setOqlQueryResponse(opsResp),
+            new ResourcePermission(Resource.DATA, Operation.READ)));
   }
 }
diff --git 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/DecodingException.java
 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/DecodingException.java
index 93cedff..47be69a 100644
--- 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/DecodingException.java
+++ 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/DecodingException.java
@@ -20,7 +20,7 @@ import org.apache.geode.annotations.Experimental;
  * This indicates an encoding type that we don't know how to handle.
  */
 @Experimental
-public class DecodingException extends Exception {
+public class DecodingException extends RuntimeException {
   public DecodingException(String message) {
     super(message);
   }
diff --git 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/EncodingException.java
 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/EncodingException.java
index 1c2c615..1f5091e 100644
--- 
a/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/EncodingException.java
+++ 
b/geode-protobuf/src/main/java/org/apache/geode/internal/protocol/protobuf/v1/serialization/exception/EncodingException.java
@@ -20,7 +20,7 @@ import org.apache.geode.annotations.Experimental;
  * This indicates a Java object that we don't know how to handle.
  */
 @Experimental
-public class EncodingException extends Exception {
+public class EncodingException extends RuntimeException {
   public EncodingException(String message) {
     super(message);
   }
diff --git 
a/geode-protobuf/src/test/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandlerIntegrationTest.java
 
b/geode-protobuf/src/test/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandlerIntegrationTest.java
new file mode 100644
index 0000000..85f04c2
--- /dev/null
+++ 
b/geode-protobuf/src/test/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandlerIntegrationTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.protocol.protobuf.v1.operations;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.query.FunctionDomainException;
+import org.apache.geode.cache.query.NameResolutionException;
+import org.apache.geode.cache.query.QueryInvocationTargetException;
+import org.apache.geode.cache.query.TypeMismatchException;
+import org.apache.geode.cache.query.data.PortfolioPdx;
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.internal.exception.InvalidExecutionContextException;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes.EncodedValue;
+import org.apache.geode.internal.protocol.protobuf.v1.MessageExecutionContext;
+import 
org.apache.geode.internal.protocol.protobuf.v1.ProtobufSerializationService;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryRequest;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryResponse;
+import org.apache.geode.internal.protocol.protobuf.v1.Result;
+import 
org.apache.geode.internal.protocol.protobuf.v1.serialization.exception.DecodingException;
+import 
org.apache.geode.internal.protocol.protobuf.v1.serialization.exception.EncodingException;
+import 
org.apache.geode.internal.protocol.protobuf.v1.state.exception.ConnectionStateException;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class OqlQueryRequestOperationHandlerIntegrationTest {
+  private Cache cache;
+
+  @Before
+  public void setUp() throws Exception {
+    cache = new CacheFactory().set(ConfigurationProperties.LOCATORS, 
"").create();
+    Region region = 
cache.createRegionFactory(RegionShortcut.LOCAL).create("region");
+
+    IntStream.range(0, 2).forEach(i -> region.put("key" + i, new 
PortfolioPdx(i)));
+  }
+
+  @After
+  public void tearDown() {
+    cache.close();
+  }
+
+  @Test
+  public void queryForSingleObject() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    checkResults("(select * from /region).size", 2);
+  }
+
+  @Test
+  public void queryForMultipleWholeObjects() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    checkResults("select ID from /region order by ID", 0, 1);
+  }
+
+  @Test
+  public void queryForMultipleProjectionFields() throws 
ConnectionStateException, DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    checkResults("select ID,status from /region order by ID", new 
EncodedValue[] {},
+        new String[] {"ID", "status"}, new Object[] {0, "active"}, new 
Object[] {1, "inactive"});
+  }
+
+  @Test
+  public void queryForSingleStruct() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    checkResults("select count(*),min(ID) from /region", new EncodedValue[] {},
+        new String[] {"0", "ID"}, new Object[] {2, 0});
+  }
+
+  @Test
+  public void queryWithBindParameters() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    checkResults("select ID,status from /region where ID=$1",
+        new EncodedValue[] {new ProtobufSerializationService().encode(0)},
+        new String[] {"ID", "status"}, new Object[] {0, "active"});
+  }
+
+  private void checkResults(final String query, final Object value)
+      throws InvalidExecutionContextException, ConnectionStateException, 
EncodingException,
+      DecodingException {
+    ProtobufSerializationService serializer = new 
ProtobufSerializationService();
+    final Result<OQLQueryResponse> results =
+        invokeHandler(query, new EncodedValue[] {}, serializer);
+
+    assertEquals(serializer.encode(value), 
results.getMessage().getSingleResult());
+  }
+
+  private void checkResults(final String query, final Object... values)
+      throws InvalidExecutionContextException, ConnectionStateException, 
EncodingException,
+      DecodingException {
+    ProtobufSerializationService serializer = new 
ProtobufSerializationService();
+    final Result<OQLQueryResponse> results =
+        invokeHandler(query, new EncodedValue[] {}, serializer);
+
+    List<EncodedValue> expected =
+        
Arrays.asList(values).stream().map(serializer::encode).collect(Collectors.toList());
+    assertEquals(expected, 
results.getMessage().getListResult().getElementList());
+  }
+
+  private void checkResults(final String query, EncodedValue[] bindParameters, 
String[] fieldnames,
+      final Object[]... rows) throws InvalidExecutionContextException, 
ConnectionStateException,
+      EncodingException, DecodingException {
+    ProtobufSerializationService serializer = new 
ProtobufSerializationService();
+    final Result<OQLQueryResponse> results = invokeHandler(query, 
bindParameters, serializer);
+
+    List<BasicTypes.EncodedValueList> expected = new ArrayList<>();
+    for (Object[] row : rows) {
+      List<EncodedValue> encodedRow =
+          
Arrays.asList(row).stream().map(serializer::encode).collect(Collectors.toList());
+      
expected.add(BasicTypes.EncodedValueList.newBuilder().addAllElement(encodedRow).build());
+    }
+
+    assertEquals(expected, results.getMessage().getTableResult().getRowList());
+    assertEquals(Arrays.asList(fieldnames),
+        results.getMessage().getTableResult().getFieldNameList());
+  }
+
+  private Result<OQLQueryResponse> invokeHandler(String query, EncodedValue[] 
bindParameters,
+      ProtobufSerializationService serializer) throws 
InvalidExecutionContextException,
+      ConnectionStateException, EncodingException, DecodingException {
+    final MessageExecutionContext context = 
mock(MessageExecutionContext.class);
+    when(context.getCache()).thenReturn((InternalCache) cache);
+    final OQLQueryRequest request = 
OQLQueryRequest.newBuilder().setQuery(query)
+        .addAllBindParameter(Arrays.asList(bindParameters)).build();
+    Result<OQLQueryResponse> result =
+        new OqlQueryRequestOperationHandler().process(serializer, request, 
context);
+
+    return result;
+  }
+}
diff --git 
a/geode-protobuf/src/test/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandlerJUnitTest.java
 
b/geode-protobuf/src/test/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandlerJUnitTest.java
new file mode 100644
index 0000000..d0b4aaf
--- /dev/null
+++ 
b/geode-protobuf/src/test/java/org/apache/geode/internal/protocol/protobuf/v1/operations/OqlQueryRequestOperationHandlerJUnitTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.protocol.protobuf.v1.operations;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.query.FunctionDomainException;
+import org.apache.geode.cache.query.NameResolutionException;
+import org.apache.geode.cache.query.Query;
+import org.apache.geode.cache.query.QueryInvocationTargetException;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.cache.query.TypeMismatchException;
+import org.apache.geode.cache.query.internal.InternalQueryService;
+import org.apache.geode.cache.query.internal.LinkedStructSet;
+import org.apache.geode.cache.query.internal.ResultsBag;
+import org.apache.geode.cache.query.internal.StructImpl;
+import org.apache.geode.cache.query.internal.types.ObjectTypeImpl;
+import org.apache.geode.cache.query.internal.types.StructTypeImpl;
+import org.apache.geode.internal.exception.InvalidExecutionContextException;
+import org.apache.geode.internal.protocol.TestExecutionContext;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+import 
org.apache.geode.internal.protocol.protobuf.v1.ProtobufSerializationService;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryRequest;
+import 
org.apache.geode.internal.protocol.protobuf.v1.RegionAPI.OQLQueryResponse;
+import org.apache.geode.internal.protocol.protobuf.v1.Result;
+import 
org.apache.geode.internal.protocol.protobuf.v1.serialization.exception.DecodingException;
+import 
org.apache.geode.internal.protocol.protobuf.v1.serialization.exception.EncodingException;
+import 
org.apache.geode.internal.protocol.protobuf.v1.state.exception.ConnectionStateException;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class OqlQueryRequestOperationHandlerJUnitTest extends 
OperationHandlerJUnitTest {
+
+  public static final String SELECT_STAR_QUERY = "select * from /region";
+  public static final String STRING_RESULT_1 = "result1";
+  public static final String STRING_RESULT_2 = "result2";
+  private InternalQueryService queryService;
+
+  @Before
+  public void setUp() throws Exception {
+    queryService = mock(InternalQueryService.class);
+    when(cacheStub.getQueryService()).thenReturn(queryService);
+    operationHandler = new OqlQueryRequestOperationHandler();
+  }
+
+  @Test
+  public void queryForSingleObject() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    Query query = mock(Query.class);
+    when(queryService.newQuery(eq(SELECT_STAR_QUERY))).thenReturn(query);
+    when(query.execute((Object[]) any())).thenReturn(STRING_RESULT_1);
+    final OQLQueryRequest request =
+        OQLQueryRequest.newBuilder().setQuery(SELECT_STAR_QUERY).build();
+    final Result<OQLQueryResponse> result = 
operationHandler.process(serializationService, request,
+        TestExecutionContext.getNoAuthCacheExecutionContext(cacheStub));
+
+    assertEquals(serializationService.encode(STRING_RESULT_1),
+        result.getMessage().getSingleResult());
+  }
+
+  @Test
+  public void queryForMultipleObjects() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    Query query = mock(Query.class);
+    when(queryService.newQuery(eq(SELECT_STAR_QUERY))).thenReturn(query);
+    SelectResults results = new ResultsBag();
+    results.setElementType(new ObjectTypeImpl());
+    results.add(STRING_RESULT_1);
+    results.add(STRING_RESULT_2);
+
+    when(query.execute((Object[]) any())).thenReturn(results);
+    final OQLQueryRequest request =
+        OQLQueryRequest.newBuilder().setQuery(SELECT_STAR_QUERY).build();
+    final Result<OQLQueryResponse> result = 
operationHandler.process(serializationService, request,
+        TestExecutionContext.getNoAuthCacheExecutionContext(cacheStub));
+
+    assertEquals(Arrays.asList(STRING_RESULT_1, STRING_RESULT_2),
+        result.getMessage().getListResult().getElementList().stream()
+            .map(serializationService::decode).collect(Collectors.toList()));
+  }
+
+  @Test
+  public void queryForMultipleStructs() throws ConnectionStateException, 
DecodingException,
+      InvalidExecutionContextException, EncodingException, 
NameResolutionException,
+      TypeMismatchException, QueryInvocationTargetException, 
FunctionDomainException {
+    Query query = mock(Query.class);
+    when(queryService.newQuery(eq(SELECT_STAR_QUERY))).thenReturn(query);
+
+
+    SelectResults results = new LinkedStructSet();
+    StructTypeImpl elementType = new StructTypeImpl(new String[] {"field1"});
+    results.setElementType(elementType);
+    results.add(new StructImpl(elementType, new Object[] {STRING_RESULT_1}));
+    results.add(new StructImpl(elementType, new Object[] {STRING_RESULT_2}));
+
+    when(query.execute((Object[]) any())).thenReturn(results);
+
+    final OQLQueryRequest request =
+        OQLQueryRequest.newBuilder().setQuery(SELECT_STAR_QUERY).build();
+
+    final Result<OQLQueryResponse> result = 
operationHandler.process(serializationService, request,
+        TestExecutionContext.getNoAuthCacheExecutionContext(cacheStub));
+
+    assertEquals(
+        Arrays.asList(
+            BasicTypes.EncodedValueList.newBuilder()
+                
.addElement(serializationService.encode(STRING_RESULT_1)).build(),
+            BasicTypes.EncodedValueList.newBuilder()
+                
.addElement(serializationService.encode(STRING_RESULT_2)).build()),
+        result.getMessage().getTableResult().getRowList());
+  }
+
+}

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to