You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by js...@apache.org on 2017/08/21 19:56:15 UTC

geode git commit: GEODE-3217: Reimplement gfsh query as a single-step command

Repository: geode
Updated Branches:
  refs/heads/release/1.2.1 828b24697 -> 070ea49d2


GEODE-3217: Reimplement gfsh query as a single-step command

(cherry picked from commit 564a94b)


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/070ea49d
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/070ea49d
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/070ea49d

Branch: refs/heads/release/1.2.1
Commit: 070ea49d2633edff9992cf1d2558b98f73553144
Parents: 828b246
Author: Jared Stewart <js...@pivotal.io>
Authored: Mon Jul 17 11:14:40 2017 -0700
Committer: Jared Stewart <js...@pivotal.io>
Committed: Mon Aug 21 12:55:47 2017 -0700

----------------------------------------------------------------------
 .../internal/cli/commands/DataCommands.java     |  78 ++----
 .../internal/cli/commands/QueryCommand.java     | 133 +++++++++
 .../internal/cli/commands/QueryInterceptor.java | 107 ++++++++
 .../internal/cli/domain/DataCommandResult.java  |  97 ++-----
 .../cli/functions/DataCommandFunction.java      | 273 +------------------
 .../management/internal/cli/shell/Gfsh.java     |  55 ++--
 .../web/controllers/DataCommandsController.java |  12 +-
 .../internal/cli/commands/QueryCommandTest.java | 267 ++++++++++++++++++
 .../cli/commands/QueryCommandUnitTest.java      |  30 ++
 .../internal/cli/result/ResultBuilderTest.java  | 131 +++++++++
 10 files changed, 749 insertions(+), 434 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
index cb9c4fe..ae65399 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
@@ -14,12 +14,30 @@
  */
 package org.apache.geode.management.internal.cli.commands;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.shiro.subject.Subject;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
 import org.apache.geode.LogWriter;
 import org.apache.geode.cache.CacheClosedException;
-import org.apache.geode.cache.CacheFactory;
 import org.apache.geode.cache.DataPolicy;
 import org.apache.geode.cache.Region;
 import org.apache.geode.cache.control.RebalanceFactory;
@@ -50,36 +68,13 @@ import org.apache.geode.management.internal.cli.functions.ExportDataFunction;
 import org.apache.geode.management.internal.cli.functions.ImportDataFunction;
 import org.apache.geode.management.internal.cli.functions.RebalanceFunction;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper;
-import org.apache.geode.management.internal.cli.multistep.CLIStep;
-import org.apache.geode.management.internal.cli.multistep.MultiStepCommand;
 import org.apache.geode.management.internal.cli.result.CompositeResultData;
 import org.apache.geode.management.internal.cli.result.ErrorResultData;
 import org.apache.geode.management.internal.cli.result.ResultBuilder;
 import org.apache.geode.management.internal.cli.result.TabularResultData;
-import org.apache.geode.management.internal.cli.shell.Gfsh;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission.Operation;
 import org.apache.geode.security.ResourcePermission.Resource;
-import org.apache.shiro.subject.Subject;
-import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
-import org.springframework.shell.core.annotation.CliCommand;
-import org.springframework.shell.core.annotation.CliOption;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 /**
  * @since GemFire 7.0
@@ -1091,41 +1086,6 @@ public class DataCommands implements GfshCommand {
     return makePresentationResult(dataResult);
   }
 
-  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_DATA, CliStrings.TOPIC_GEODE_REGION})
-  @MultiStepCommand
-  @CliCommand(value = {CliStrings.QUERY}, help = CliStrings.QUERY__HELP)
-  public Object query(
-      @CliOption(key = CliStrings.QUERY__QUERY, help = CliStrings.QUERY__QUERY__HELP,
-          mandatory = true) final String query,
-      @CliOption(key = CliStrings.QUERY__STEPNAME, help = "Step name",
-          unspecifiedDefaultValue = CliStrings.QUERY__STEPNAME__DEFAULTVALUE) String stepName,
-      @CliOption(key = CliStrings.QUERY__INTERACTIVE, help = CliStrings.QUERY__INTERACTIVE__HELP,
-          unspecifiedDefaultValue = "true") final boolean interactive) {
-
-    if (!CliUtil.isGfshVM() && stepName.equals(CliStrings.QUERY__STEPNAME__DEFAULTVALUE)) {
-      return ResultBuilder.createInfoResult(CliStrings.QUERY__MSG__NOT_SUPPORTED_ON_MEMBERS);
-    }
-
-    Object[] arguments = new Object[] {query, stepName, interactive};
-    CLIStep exec = new DataCommandFunction.SelectExecStep(arguments);
-    CLIStep display = new DataCommandFunction.SelectDisplayStep(arguments);
-    CLIStep move = new DataCommandFunction.SelectMoveStep(arguments);
-    CLIStep quit = new DataCommandFunction.SelectQuitStep(arguments);
-    CLIStep[] steps = {exec, display, move, quit};
-    return CLIMultiStepHelper.chooseStep(steps, stepName);
-  }
-
-  @CliAvailabilityIndicator({CliStrings.REBALANCE, CliStrings.GET, CliStrings.PUT,
-      CliStrings.REMOVE, CliStrings.LOCATE_ENTRY, CliStrings.QUERY, CliStrings.IMPORT_DATA,
-      CliStrings.EXPORT_DATA})
-  public boolean dataCommandsAvailable() {
-    boolean isAvailable = true; // always available on server
-    if (CliUtil.isGfshVM()) { // in gfsh check if connected
-      isAvailable = getGfsh() != null && getGfsh().isConnectedAndReady();
-    }
-    return isAvailable;
-  }
-
   private static class MemberPRInfo {
     public ArrayList<DistributedMember> dsMemberList;
     public String region;

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java
new file mode 100644
index 0000000..07d8f9d
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java
@@ -0,0 +1,133 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.apache.shiro.subject.Subject;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.query.QueryInvalidException;
+import org.apache.geode.cache.query.internal.CompiledValue;
+import org.apache.geode.cache.query.internal.QCompiler;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.security.IntegratedSecurityService;
+import org.apache.geode.management.cli.CliMetaData;
+import org.apache.geode.management.cli.ConverterHint;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.domain.DataCommandRequest;
+import org.apache.geode.management.internal.cli.domain.DataCommandResult;
+import org.apache.geode.management.internal.cli.functions.DataCommandFunction;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.remote.CommandExecutionContext;
+import org.apache.geode.management.internal.cli.result.CompositeResultData;
+import org.apache.geode.management.internal.cli.result.ResultBuilder;
+
+public class QueryCommand implements GfshCommand {
+  private static final Logger logger = LogService.getLogger();
+
+  @CliCommand(value = "query", help = CliStrings.QUERY__HELP)
+  @CliMetaData(interceptor = "org.apache.geode.management.internal.cli.commands.QueryInterceptor")
+  public Result query(
+      @CliOption(key = CliStrings.QUERY__QUERY, help = CliStrings.QUERY__QUERY__HELP,
+          mandatory = true) final String query,
+      @CliOption(key = "file", help = "File in which to output the results.",
+          optionContext = ConverterHint.FILE) final File outputFile,
+      @CliOption(key = CliStrings.QUERY__INTERACTIVE, unspecifiedDefaultValue = "false",
+          help = CliStrings.QUERY__INTERACTIVE__HELP) final boolean interactive) {
+    DataCommandResult dataResult = select(query);
+    CompositeResultData rd = dataResult.toSelectCommandResult();
+    return ResultBuilder.buildResult(rd);
+  }
+
+  private DataCommandResult select(String query) {
+    InternalCache cache = (InternalCache) CacheFactory.getAnyInstance();
+    DataCommandResult dataResult;
+
+    if (StringUtils.isEmpty(query)) {
+      dataResult = DataCommandResult.createSelectInfoResult(null, null, -1, null,
+          CliStrings.QUERY__MSG__QUERY_EMPTY, false);
+      return dataResult;
+    }
+
+    Object array[] = DataCommands.replaceGfshEnvVar(query, CommandExecutionContext.getShellEnv());
+    query = (String) array[1];
+    boolean limitAdded = false;
+
+    if (!StringUtils.containsIgnoreCase(query, " limit")
+        && !StringUtils.containsIgnoreCase(query, " count(")) {
+      query = query + " limit " + CommandExecutionContext.getShellFetchSize();
+      limitAdded = true;
+    }
+
+    @SuppressWarnings("deprecation")
+    QCompiler compiler = new QCompiler();
+    Set<String> regionsInQuery;
+    try {
+      CompiledValue compiledQuery = compiler.compileQuery(query);
+      Set<String> regions = new HashSet<>();
+      compiledQuery.getRegionsInQuery(regions, null);
+
+      // authorize data read on these regions
+      for (String region : regions) {
+        IntegratedSecurityService.getSecurityService().authorizeRegionRead(region);
+      }
+
+      regionsInQuery = Collections.unmodifiableSet(regions);
+      if (regionsInQuery.size() > 0) {
+        Set<DistributedMember> members =
+            DataCommands.getQueryRegionsAssociatedMembers(regionsInQuery, cache, false);
+        if (members != null && members.size() > 0) {
+          DataCommandFunction function = new DataCommandFunction();
+          DataCommandRequest request = new DataCommandRequest();
+          request.setCommand(CliStrings.QUERY);
+          request.setQuery(query);
+          Subject subject = IntegratedSecurityService.getSecurityService().getSubject();
+          if (subject != null) {
+            request.setPrincipal(subject.getPrincipal());
+          }
+          dataResult = DataCommands.callFunctionForRegion(request, function, members);
+          dataResult.setInputQuery(query);
+          if (limitAdded) {
+            dataResult.setLimit(CommandExecutionContext.getShellFetchSize());
+          }
+          return dataResult;
+        } else {
+          return DataCommandResult.createSelectInfoResult(null, null, -1, null, CliStrings
+              .format(CliStrings.QUERY__MSG__REGIONS_NOT_FOUND, regionsInQuery.toString()), false);
+        }
+      } else {
+        return DataCommandResult.createSelectInfoResult(null, null, -1, null,
+            CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY,
+                "Region mentioned in query probably missing /"),
+            false);
+      }
+    } catch (QueryInvalidException qe) {
+      logger.error("{} Failed Error {}", query, qe.getMessage(), qe);
+      return DataCommandResult.createSelectInfoResult(null, null, -1, null,
+          CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY, qe.getMessage()), false);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java
new file mode 100644
index 0000000..559965f
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java
@@ -0,0 +1,107 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.SystemUtils;
+import org.springframework.shell.event.ParseResult;
+
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor;
+import org.apache.geode.management.internal.cli.GfshParseResult;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.result.CompositeResultData;
+import org.apache.geode.management.internal.cli.result.InfoResultData;
+import org.apache.geode.management.internal.cli.result.ResultBuilder;
+import org.apache.geode.management.internal.cli.result.TabularResultData;
+
+public class QueryInterceptor extends AbstractCliAroundInterceptor {
+  public static final String FILE_ALREADY_EXISTS_MESSAGE =
+      "The specified output file already exists.";
+
+  @Override
+  public Result preExecution(GfshParseResult parseResult) {
+    File outputFile = getOutputFile(parseResult);
+
+    if (outputFile != null && outputFile.exists()) {
+      return ResultBuilder.createUserErrorResult(FILE_ALREADY_EXISTS_MESSAGE);
+    }
+
+    return ResultBuilder.createInfoResult("");
+  }
+
+  @Override
+  public Result postExecution(GfshParseResult parseResult, Result result, Path tempFile) {
+    File outputFile = getOutputFile(parseResult);
+
+    if (outputFile == null) {
+      return result;
+    }
+
+    CommandResult commandResult = (CommandResult) result;
+    CompositeResultData resultData = (CompositeResultData) commandResult.getResultData();
+    CompositeResultData.SectionResultData sectionResultData = resultData.retrieveSectionByIndex(0);
+
+    String limit = sectionResultData.retrieveString("Limit");
+    String resultString = sectionResultData.retrieveString("Result");
+    String rows = sectionResultData.retrieveString("Rows");
+
+    if ("false".equalsIgnoreCase(resultString)) {
+      return result;
+    }
+
+    TabularResultData tabularResultData = sectionResultData.retrieveTableByIndex(0);
+    CommandResult resultTable = new CommandResult(tabularResultData);
+    try {
+      writeResultTableToFile(outputFile, resultTable);
+      // return a result w/ message explaining limit
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+
+    InfoResultData infoResultData = ResultBuilder.createInfoResultData();
+    infoResultData.addLine("Result : " + resultString);
+    if (StringUtils.isNotBlank(limit)) {
+      infoResultData.addLine("Limit  : " + limit);
+    }
+    infoResultData.addLine("Rows   : " + rows);
+    infoResultData.addLine(SystemUtils.LINE_SEPARATOR);
+    infoResultData.addLine("Query results output to " + outputFile.getAbsolutePath());
+
+    return new CommandResult(infoResultData);
+  }
+
+  private File getOutputFile(ParseResult parseResult) {
+    return (File) parseResult.getArguments()[1];
+  }
+
+  private void writeResultTableToFile(File file, CommandResult commandResult) throws IOException {
+    try (FileWriter fileWriter = new FileWriter(file)) {
+      while (commandResult.hasNextLine()) {
+        fileWriter.write(commandResult.nextLine());
+
+        if (commandResult.hasNextLine()) {
+          fileWriter.write(SystemUtils.LINE_SEPARATOR);
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
index fe88fc9..b682767 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
@@ -14,16 +14,26 @@
  */
 package org.apache.geode.management.internal.cli.domain;
 
-import static org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper.createBannerResult;
-import static org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper.createPageResult;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.json.JSONObject;
+
 import org.apache.geode.DataSerializer;
 import org.apache.geode.internal.ClassPathLoader;
 import org.apache.geode.management.cli.Result;
 import org.apache.geode.management.internal.cli.GfshParser;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.json.GfJsonArray;
 import org.apache.geode.management.internal.cli.json.GfJsonException;
 import org.apache.geode.management.internal.cli.json.GfJsonObject;
 import org.apache.geode.management.internal.cli.result.CompositeResultData;
@@ -31,26 +41,12 @@ import org.apache.geode.management.internal.cli.result.CompositeResultData.Secti
 import org.apache.geode.management.internal.cli.result.ResultBuilder;
 import org.apache.geode.management.internal.cli.result.TabularResultData;
 import org.apache.geode.management.internal.cli.util.JsonUtil;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.json.JSONObject;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
 
 
 /**
  * Domain object used for Data Commands Functions TODO : Implement DataSerializable
  */
 public class DataCommandResult implements /* Data */ Serializable {
-
   private static Logger logger = LogManager.getLogger();
 
   private static final long serialVersionUID = 1L;
@@ -560,7 +556,7 @@ public class DataCommandResult implements /* Data */ Serializable {
         section.addData("Message", infoString);
       }
       if (inputQuery != null) {
-        if (this.limit != -1) {
+        if (this.limit > 0) {
           section.addData("Limit", this.limit);
         }
         if (this.selectResult != null) {
@@ -575,67 +571,6 @@ public class DataCommandResult implements /* Data */ Serializable {
     }
   }
 
-  /**
-   * This method returns a "Page" as dictated by arguments startCount and endCount. Returned result
-   * is not standard CommandResult and its consumed by Display Step
-   */
-  @SuppressWarnings({"rawtypes", "unchecked"})
-  public Result pageResult(int startCount, int endCount, String step) {
-    List<String> fields = new ArrayList<>();
-    List values = new ArrayList<String>();
-    fields.add(RESULT_FLAG);
-    values.add(operationCompletedSuccessfully);
-    fields.add(QUERY_PAGE_START);
-    values.add(startCount);
-    fields.add(QUERY_PAGE_END);
-    values.add(endCount);
-    if (errorString != null) {
-      fields.add("Message");
-      values.add(errorString);
-      return createBannerResult(fields, values, step);
-    } else {
-
-      if (infoString != null) {
-        fields.add("Message");
-        values.add(infoString);
-      }
-
-      if (selectResult != null) {
-        try {
-          TabularResultData table = ResultBuilder.createTabularResultData();
-          String[] headers;
-          Object[][] rows;
-          int rowCount = buildTable(table, startCount, endCount);
-          GfJsonArray array = table.getHeaders();
-          headers = new String[array.size()];
-          rows = new Object[rowCount][array.size()];
-          for (int i = 0; i < array.size(); i++) {
-            headers[i] = (String) array.get(i);
-            List<String> list = table.retrieveAllValues(headers[i]);
-            for (int j = 0; j < list.size(); j++) {
-              rows[j][i] = list.get(j);
-            }
-          }
-          fields.add(NUM_ROWS);
-          values.add((selectResult == null) ? 0 : selectResult.size());
-          if (queryTraceString != null) {
-            fields.add(QUERY_TRACE);
-            values.add(queryTraceString);
-          }
-          return createPageResult(fields, values, step, headers, rows);
-        } catch (GfJsonException e) {
-          String[] headers = new String[] {"Error"};
-          Object[][] rows = {{e.getMessage()}};
-          String fieldsArray[] = {QUERY_PAGE_START, QUERY_PAGE_END};
-          Object valuesArray[] = {startCount, endCount};
-          return createPageResult(fieldsArray, valuesArray, step, headers, rows);
-        }
-      } else {
-        return createBannerResult(fields, values, step);
-      }
-    }
-  }
-
   private int buildTable(TabularResultData table, int startCount, int endCount) {
     // Three steps:
     // 1a. Convert each row object to a Json object.
@@ -729,6 +664,10 @@ public class DataCommandResult implements /* Data */ Serializable {
     this.inputQuery = inputQuery;
   }
 
+  public void setLimit(int limit) {
+    this.limit = limit;
+  }
+
   public static class KeyInfo implements /* Data */ Serializable {
 
     private String memberId;

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
index e2164a3..4199f9e 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
@@ -14,7 +14,18 @@
  */
 package org.apache.geode.management.internal.cli.functions;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.json.JSONArray;
+
 import org.apache.geode.cache.CacheFactory;
 import org.apache.geode.cache.DataPolicy;
 import org.apache.geode.cache.Region;
@@ -24,16 +35,13 @@ import org.apache.geode.cache.partition.PartitionRegionHelper;
 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.QueryInvalidException;
 import org.apache.geode.cache.query.QueryInvocationTargetException;
 import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.cache.query.SelectResults;
 import org.apache.geode.cache.query.Struct;
 import org.apache.geode.cache.query.TypeMismatchException;
-import org.apache.geode.cache.query.internal.CompiledValue;
 import org.apache.geode.cache.query.internal.DefaultQuery;
 import org.apache.geode.cache.query.internal.IndexTrackingQueryObserver;
-import org.apache.geode.cache.query.internal.QCompiler;
 import org.apache.geode.cache.query.internal.QueryObserver;
 import org.apache.geode.cache.query.internal.QueryObserverHolder;
 import org.apache.geode.cache.query.internal.StructImpl;
@@ -46,37 +54,14 @@ import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.internal.cache.PartitionedRegion;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.security.SecurityService;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.CliUtil;
-import org.apache.geode.management.internal.cli.commands.DataCommands;
 import org.apache.geode.management.internal.cli.domain.DataCommandRequest;
 import org.apache.geode.management.internal.cli.domain.DataCommandResult;
 import org.apache.geode.management.internal.cli.domain.DataCommandResult.SelectResultRow;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
 import org.apache.geode.management.internal.cli.json.GfJsonException;
 import org.apache.geode.management.internal.cli.json.GfJsonObject;
-import org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper;
-import org.apache.geode.management.internal.cli.remote.CommandExecutionContext;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.management.internal.cli.result.CompositeResultData;
-import org.apache.geode.management.internal.cli.result.CompositeResultData.SectionResultData;
-import org.apache.geode.management.internal.cli.result.ResultBuilder;
-import org.apache.geode.management.internal.cli.shell.Gfsh;
 import org.apache.geode.management.internal.cli.util.JsonUtil;
 import org.apache.geode.pdx.PdxInstance;
-import org.apache.logging.log4j.Logger;
-import org.apache.shiro.subject.Subject;
-import org.json.JSONArray;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @since GemFire 7.0
@@ -88,10 +73,6 @@ public class DataCommandFunction extends FunctionAdapter implements InternalEnti
 
   private boolean optimizeForWrite = false;
 
-  protected static final String SELECT_STEP_DISPLAY = "SELECT_DISPLAY";
-  protected static final String SELECT_STEP_MOVE = "SELECT_PAGE_MOVE";
-  protected static final String SELECT_STEP_END = "SELECT_END";
-  protected static final String SELECT_STEP_EXEC = "SELECT_EXEC";
   private static final int NESTED_JSON_LENGTH = 20;
 
   // this needs to be static so that it won't get serialized
@@ -802,7 +783,6 @@ public class DataCommandFunction extends FunctionAdapter implements InternalEnti
   }
 
 
-
   /**
    * Returns a sorted list of all region full paths found in the specified cache.
    * 
@@ -836,237 +816,6 @@ public class DataCommandFunction extends FunctionAdapter implements InternalEnti
     return list;
   }
 
-  private static DataCommandResult cachedResult = null;
-
-  public static class SelectDisplayStep extends CLIMultiStepHelper.LocalStep {
-
-    public SelectDisplayStep(Object[] arguments) {
-      super(SELECT_STEP_DISPLAY, arguments);
-    }
-
-    @Override
-    public Result exec() {
-      boolean interactive = (Boolean) commandArguments[2];
-      GfJsonObject args = CLIMultiStepHelper.getStepArgs();
-      int startCount = args.getInt(DataCommandResult.QUERY_PAGE_START);
-      int endCount = args.getInt(DataCommandResult.QUERY_PAGE_END);
-      int rows = args.getInt(DataCommandResult.NUM_ROWS); // returns Zero if no rows added so it
-      // works.
-      boolean flag = args.getBoolean(DataCommandResult.RESULT_FLAG);
-      CommandResult commandResult = CLIMultiStepHelper.getDisplayResultFromArgs(args);
-      Gfsh.println();
-      while (commandResult.hasNextLine()) {
-        Gfsh.println(commandResult.nextLine());
-      }
-
-      if (flag) {
-        boolean paginationNeeded = startCount < rows && endCount < rows && interactive;
-        if (paginationNeeded) {
-          while (true) {
-            String message = ("Press n to move to next page, q to quit and p to previous page : ");
-            try {
-              String step = Gfsh.getCurrentInstance().interact(message);
-              if ("n".equals(step)) {
-                int nextStart = startCount + getPageSize();
-                return CLIMultiStepHelper.createBannerResult(
-                    new String[] {DataCommandResult.QUERY_PAGE_START,
-                        DataCommandResult.QUERY_PAGE_END,},
-                    new Object[] {nextStart, (nextStart + getPageSize())}, SELECT_STEP_MOVE);
-              } else if ("p".equals(step)) {
-                int nextStart = startCount - getPageSize();
-                if (nextStart < 0) {
-                  nextStart = 0;
-                }
-                return CLIMultiStepHelper.createBannerResult(
-                    new String[] {DataCommandResult.QUERY_PAGE_START,
-                        DataCommandResult.QUERY_PAGE_END},
-                    new Object[] {nextStart, (nextStart + getPageSize())}, SELECT_STEP_MOVE);
-              } else if ("q".equals(step)) {
-                return CLIMultiStepHelper.createBannerResult(new String[] {}, new Object[] {},
-                    SELECT_STEP_END);
-              } else {
-                Gfsh.println("Unknown option ");
-              }
-            } catch (IOException e) {
-              throw new RuntimeException(e);
-            }
-          }
-        }
-      }
-      return CLIMultiStepHelper.createBannerResult(new String[] {}, new Object[] {},
-          SELECT_STEP_END);
-    }
-  }
-
-  public static class SelectMoveStep extends CLIMultiStepHelper.RemoteStep {
-
-    private static final long serialVersionUID = 1L;
-
-    public SelectMoveStep(Object[] arguments) {
-      super(SELECT_STEP_MOVE, arguments);
-    }
-
-    @Override
-    public Result exec() {
-      GfJsonObject args = CLIMultiStepHelper.getStepArgs();
-      int startCount = args.getInt(DataCommandResult.QUERY_PAGE_START);
-      int endCount = args.getInt(DataCommandResult.QUERY_PAGE_END);
-      return cachedResult.pageResult(startCount, endCount, SELECT_STEP_DISPLAY);
-    }
-  }
-
-  public static class SelectExecStep extends CLIMultiStepHelper.RemoteStep {
-
-    private static final long serialVersionUID = 1L;
-
-    private static SecurityService securityService = SecurityService.getSecurityService();
-
-    public SelectExecStep(Object[] arguments) {
-      super(SELECT_STEP_EXEC, arguments);
-    }
-
-    @Override
-    public Result exec() {
-      String remainingQuery = (String) commandArguments[0];
-      boolean interactive = (Boolean) commandArguments[2];
-      DataCommandResult result = _select(remainingQuery);
-      int endCount = 0;
-      cachedResult = result;
-      if (interactive) {
-        endCount = getPageSize();
-      } else {
-        if (result.getSelectResult() != null) {
-          endCount = result.getSelectResult().size();
-        }
-      }
-      if (interactive) {
-        return result.pageResult(0, endCount, SELECT_STEP_DISPLAY);
-      } else {
-        return CLIMultiStepHelper.createBannerResult(new String[] {}, new Object[] {},
-            SELECT_STEP_END);
-      }
-    }
-
-    public DataCommandResult _select(String query) {
-      InternalCache cache = (InternalCache) CacheFactory.getAnyInstance();
-      DataCommandResult dataResult;
-
-      if (StringUtils.isEmpty(query)) {
-        dataResult = DataCommandResult.createSelectInfoResult(null, null, -1, null,
-            CliStrings.QUERY__MSG__QUERY_EMPTY, false);
-        return dataResult;
-      }
-
-      Object array[] = DataCommands.replaceGfshEnvVar(query, CommandExecutionContext.getShellEnv());
-      query = (String) array[1];
-      query = addLimit(query);
-
-      @SuppressWarnings("deprecation")
-      QCompiler compiler = new QCompiler();
-      Set<String> regionsInQuery;
-      try {
-        CompiledValue compiledQuery = compiler.compileQuery(query);
-        Set<String> regions = new HashSet<>();
-        compiledQuery.getRegionsInQuery(regions, null);
-
-        // authorize data read on these regions
-        for (String region : regions) {
-          securityService.authorizeRegionRead(region);
-        }
-
-        regionsInQuery = Collections.unmodifiableSet(regions);
-        if (regionsInQuery.size() > 0) {
-          Set<DistributedMember> members =
-              DataCommands.getQueryRegionsAssociatedMembers(regionsInQuery, cache, false);
-          if (members != null && members.size() > 0) {
-            DataCommandFunction function = new DataCommandFunction();
-            DataCommandRequest request = new DataCommandRequest();
-            request.setCommand(CliStrings.QUERY);
-            request.setQuery(query);
-            Subject subject = securityService.getSubject();
-            if (subject != null) {
-              request.setPrincipal(subject.getPrincipal());
-            }
-            dataResult = DataCommands.callFunctionForRegion(request, function, members);
-            dataResult.setInputQuery(query);
-            return dataResult;
-          } else {
-            return DataCommandResult.createSelectInfoResult(null, null, -1, null, CliStrings.format(
-                CliStrings.QUERY__MSG__REGIONS_NOT_FOUND, regionsInQuery.toString()), false);
-          }
-        } else {
-          return DataCommandResult.createSelectInfoResult(null, null, -1, null,
-              CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY,
-                  "Region mentioned in query probably missing /"),
-              false);
-        }
-      } catch (QueryInvalidException qe) {
-        logger.error("{} Failed Error {}", query, qe.getMessage(), qe);
-        return DataCommandResult.createSelectInfoResult(null, null, -1, null,
-            CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY, qe.getMessage()), false);
-      }
-    }
-
-    private String addLimit(String query) {
-      if (StringUtils.containsIgnoreCase(query, " limit")
-          || StringUtils.containsIgnoreCase(query, " count(")) {
-        return query;
-      }
-      return query + " limit " + getFetchSize();
-    }
-  }
-
-  public static class SelectQuitStep extends CLIMultiStepHelper.RemoteStep {
-
-    public SelectQuitStep(Object[] arguments) {
-      super(SELECT_STEP_END, arguments);
-    }
-
-    private static final long serialVersionUID = 1L;
-
-    @Override
-    public Result exec() {
-      boolean interactive = (Boolean) commandArguments[2];
-      GfJsonObject args = CLIMultiStepHelper.getStepArgs();
-      DataCommandResult dataResult = cachedResult;
-      cachedResult = null;
-      if (interactive) {
-        return CLIMultiStepHelper.createEmptyResult("END");
-      } else {
-        CompositeResultData rd = dataResult.toSelectCommandResult();
-        SectionResultData section = rd.addSection(CLIMultiStepHelper.STEP_SECTION);
-        section.addData(CLIMultiStepHelper.NEXT_STEP_NAME, "END");
-        return ResultBuilder.buildResult(rd);
-      }
-    }
-  }
-
-  public static int getPageSize() {
-    int pageSize = -1;
-    Map<String, String> session;
-    if (CliUtil.isGfshVM()) {
-      session = Gfsh.getCurrentInstance().getEnv();
-    } else {
-      session = CommandExecutionContext.getShellEnv();
-    }
-    if (session != null) {
-      String size = session.get(Gfsh.ENV_APP_COLLECTION_LIMIT);
-      if (StringUtils.isEmpty(size)) {
-        pageSize = Gfsh.DEFAULT_APP_COLLECTION_LIMIT;
-      } else {
-        pageSize = Integer.parseInt(size);
-      }
-    }
-    if (pageSize == -1) {
-      pageSize = Gfsh.DEFAULT_APP_COLLECTION_LIMIT;
-    }
-    return pageSize;
-  }
-
-  private static int getFetchSize() {
-    return CommandExecutionContext.getShellFetchSize();
-  }
-
   public static String getLogMessage(QueryObserver observer, long startTime, String query) {
     String usedIndexesString = null;
     float time = 0.0f;

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
index c5ff6b6..2124d64 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
@@ -14,8 +14,35 @@
  */
 package org.apache.geode.management.internal.cli.shell;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
 import jline.Terminal;
 import jline.console.ConsoleReader;
+import org.springframework.shell.core.AbstractShell;
+import org.springframework.shell.core.ExecutionStrategy;
+import org.springframework.shell.core.ExitShellRequest;
+import org.springframework.shell.core.JLineLogHandler;
+import org.springframework.shell.core.JLineShell;
+import org.springframework.shell.core.Parser;
+import org.springframework.shell.event.ShellStatus.Status;
+
 import org.apache.geode.internal.Banner;
 import org.apache.geode.internal.GemFireVersion;
 import org.apache.geode.internal.lang.ClassUtils;
@@ -39,32 +66,6 @@ import org.apache.geode.management.internal.cli.shell.jline.GfshHistory;
 import org.apache.geode.management.internal.cli.shell.jline.GfshUnsupportedTerminal;
 import org.apache.geode.management.internal.cli.shell.unsafe.GfshSignalHandler;
 import org.apache.geode.management.internal.cli.util.CommentSkipHelper;
-import org.springframework.shell.core.AbstractShell;
-import org.springframework.shell.core.ExecutionStrategy;
-import org.springframework.shell.core.ExitShellRequest;
-import org.springframework.shell.core.JLineLogHandler;
-import org.springframework.shell.core.JLineShell;
-import org.springframework.shell.core.Parser;
-import org.springframework.shell.event.ShellStatus.Status;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.URL;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
 
 /**
  * Extends an interactive shell provided by
@@ -84,7 +85,7 @@ import java.util.logging.Logger;
  * @since GemFire 7.0
  */
 public class Gfsh extends JLineShell {
-  public static final int DEFAULT_APP_FETCH_SIZE = 1000;
+  public static final int DEFAULT_APP_FETCH_SIZE = 100;
   public static final int DEFAULT_APP_LAST_EXIT_STATUS = 0;
   public static final int DEFAULT_APP_COLLECTION_LIMIT = 20;
   public static final boolean DEFAULT_APP_QUIET_EXECUTION = false;

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
index ce2ed54..9312051 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
@@ -14,9 +14,8 @@
  */
 package org.apache.geode.management.internal.web.controllers;
 
-import org.apache.geode.internal.lang.StringUtils;
-import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import java.util.concurrent.Callable;
+
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -26,7 +25,9 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.context.request.WebRequest;
 
-import java.util.concurrent.Callable;
+import org.apache.geode.internal.lang.StringUtils;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
 
 /**
  * The DataCommandsController class implements GemFire Management REST API web service endpoints for
@@ -185,8 +186,6 @@ public class DataCommandsController extends AbstractCommandsController {
   @RequestMapping(method = RequestMethod.GET, value = "/regions/data/query")
   public Callable<ResponseEntity<String>> query(final WebRequest request,
       @RequestParam(CliStrings.QUERY__QUERY) final String oql,
-      @RequestParam(value = CliStrings.QUERY__STEPNAME,
-          defaultValue = CliStrings.QUERY__STEPNAME__DEFAULTVALUE) final String stepName,
       @RequestParam(value = CliStrings.QUERY__INTERACTIVE,
           defaultValue = "true") final Boolean interactive) {
     // logRequest(request);
@@ -194,7 +193,6 @@ public class DataCommandsController extends AbstractCommandsController {
     final CommandStringBuilder command = new CommandStringBuilder(CliStrings.QUERY);
 
     command.addOption(CliStrings.QUERY__QUERY, decode(oql));
-    command.addOption(CliStrings.QUERY__STEPNAME, stepName);
     command.addOption(CliStrings.QUERY__INTERACTIVE,
         String.valueOf(Boolean.TRUE.equals(interactive)));
 

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java
new file mode 100644
index 0000000..8f30bfe
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import com.google.common.io.Files;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.shell.Gfsh;
+import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
+import org.apache.geode.test.dunit.rules.ServerStarterRule;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+
+@Category(IntegrationTest.class)
+@RunWith(Parameterized.class)
+public class QueryCommandTest {
+  @Parameterized.Parameters(name = "Connect via http: {0}")
+  public static Object[] data() {
+    return new Object[] {true, false};
+  }
+
+  @Parameterized.Parameter
+  public boolean useHttp;
+
+  @ClassRule
+  public static ServerStarterRule server =
+      new ServerStarterRule().withJMXManager().withRegion(RegionShortcut.REPLICATE, "simpleRegion")
+          .withRegion(RegionShortcut.REPLICATE, "complexRegion");
+
+  @Rule
+  public GfshShellConnectionRule gfsh = new GfshShellConnectionRule();
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @BeforeClass
+  public static void populateRegions() {
+    Cache cache = server.getCache();
+    Region<String, String> simpleRegion = cache.getRegion("simpleRegion");
+    Region<String, Customer> complexRegion = cache.getRegion("complexRegion");
+
+    for (int i = 0; i < Gfsh.DEFAULT_APP_FETCH_SIZE + 1; i++) {
+      String key = "key" + i;
+
+      simpleRegion.put(key, "value" + i);
+      complexRegion.put(key, new Customer("name" + i, "address" + i));
+    }
+  }
+
+  @Before
+  public void connect() throws Exception {
+    if (useHttp) {
+      gfsh.connectAndVerify(server.getHttpPort(), GfshShellConnectionRule.PortType.http);
+    } else {
+      gfsh.connectAndVerify(server.getJmxPort(), GfshShellConnectionRule.PortType.jmxManger);
+    }
+  }
+
+  @Test
+  public void doesShowLimitIfLimitNotInQuery() throws Exception {
+    String output = gfsh.execute("query --query='select * from /simpleRegion'");
+    assertThat(output).contains("Rows   : " + Gfsh.DEFAULT_APP_FETCH_SIZE);
+    assertThat(output).contains("Limit  : " + Gfsh.DEFAULT_APP_FETCH_SIZE);
+    assertThatOutputHasResult(output);
+  }
+
+  @Test
+  public void doesNotShowLimitIfLimitInQuery() throws Exception {
+    String output = gfsh.execute("query --query='select * from /simpleRegion limit 50'");
+    assertThat(output).contains("Rows   : 50");
+    assertThat(output).doesNotContain("Limit");
+    assertThatOutputHasResult(output);
+  }
+
+  @Test
+  public void invalidQueryShouldNotCreateFile() throws Exception {
+    File outputFile = temporaryFolder.newFile("queryOutput.txt");
+    FileUtils.deleteQuietly(outputFile);
+
+    String output =
+        gfsh.execute("query --query='invalid query' --file=" + outputFile.getAbsolutePath());
+    assertThat(outputFile).doesNotExist();
+
+    assertThatOutputHasNoResult(output);
+    assertThat(output).doesNotContain("Query results output to");
+  }
+
+  @Test
+  public void queryWithInvalidRegionNameDoesNotCreateFile() throws Exception {
+    File outputFile = temporaryFolder.newFile("queryOutput.txt");
+    FileUtils.deleteQuietly(outputFile);
+
+    String output = gfsh.execute(
+        "query --query='select * from /nonExistentRegion' --file=" + outputFile.getAbsolutePath());
+    assertThat(outputFile).doesNotExist();
+
+    assertThatOutputHasNoResult(output);
+    assertThat(output).doesNotContain("Query results output to");
+  }
+
+  @Test
+  public void outputToFileStillDisplaysResultMetaData() throws Exception {
+    File outputFile = temporaryFolder.newFile("queryOutput.txt");
+    FileUtils.deleteQuietly(outputFile);
+
+    String output = gfsh.execute(
+        "query --query='select * from /simpleRegion' --file=" + outputFile.getAbsolutePath());
+
+    assertThat(output).contains("Rows");
+    assertThat(output).contains("Limit");
+    assertThat(output).contains("Query results output to");
+    assertThatOutputHasResult(output);
+  }
+
+  @Test
+  public void doesNotOverwriteExistingFile() throws Exception {
+    File outputFile = temporaryFolder.newFile("queryOutput.txt");
+    assertThat(outputFile).exists();
+
+    CommandResult result = gfsh.executeCommand(
+        "query --query='select * from /simpleRegion' --file=" + outputFile.getAbsolutePath());
+
+    assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+    assertThat(result.getContent().getString("message"))
+        .contains("The specified output file already exists.");
+  }
+
+  @Test
+  public void canOutputSimpleRegionToFile() throws Exception {
+    File outputFile = temporaryFolder.newFile("queryOutput.txt");
+    FileUtils.deleteQuietly(outputFile);
+
+    CommandResult result = gfsh.executeAndVerifyCommand(
+        "query --query='select * from /simpleRegion' --file=" + outputFile.getAbsolutePath());
+    assertThat(outputFile).exists();
+    assertThat(result.getContent().toString()).contains(outputFile.getAbsolutePath());
+
+    List<String> lines = Files.readLines(outputFile, StandardCharsets.UTF_8);
+
+    assertThat(lines.get(0)).isEqualTo("Result");
+    assertThat(lines.get(1)).isEqualTo("--------");
+    lines.subList(2, lines.size()).forEach(line -> assertThat(line).matches("value\\d+"));
+  }
+
+  @Test
+  public void canOutputComplexRegionToFile() throws Exception {
+    File outputFile = temporaryFolder.newFile("queryOutput.txt");
+    FileUtils.deleteQuietly(outputFile);
+
+    CommandResult result = gfsh.executeAndVerifyCommand(
+        "query --query='select c.name, c.address from /complexRegion c' --file="
+            + outputFile.getAbsolutePath());
+    assertThat(outputFile).exists();
+    assertThat(result.getContent().toString()).contains(outputFile.getAbsolutePath());
+
+    List<String> lines = Files.readLines(outputFile, StandardCharsets.UTF_8);
+
+    assertThat(lines.get(0)).containsPattern("name\\s+\\|\\s+address");
+    lines.subList(2, lines.size())
+        .forEach(line -> assertThat(line).matches("name\\d+\\s+\\|\\s+address\\d+"));
+  }
+
+  @Test
+  public void outputDisplaysResultsFromComplexRegion() throws Exception {
+    String result = gfsh.execute("query --query='select c.name, c.address from /complexRegion c'");
+
+    String[] resultLines = splitOnLineBreaks(result);
+
+    assertThat(resultLines[0]).containsPattern("Result\\s+:\\s+true");
+    assertThat(resultLines[1]).containsPattern("Limit\\s+:\\s+100");
+    assertThat(resultLines[2]).containsPattern("Rows\\s+:\\s+100");
+    assertThat(resultLines[3]).containsPattern("name\\s+\\|\\s+address");
+    Arrays.asList(resultLines).subList(5, resultLines.length)
+        .forEach(line -> assertThat(line).matches("name\\d+\\s+\\|\\s+address\\d+"));
+  }
+
+  @Test
+  public void queryWithInvalidRegionNameGivesDescriptiveErrorMessage() throws Exception {
+    String output = gfsh.execute("query --query='select * from /nonExistentRegion'");
+    assertThatOutputHasNoResult(output);
+    assertThatOutputHasMessage(output,
+        "Cannot find regions <[/nonExistentRegion]> in any of the members");
+  }
+
+  @Test
+  public void invalidQueryGivesDescriptiveErrorMessage() throws Exception {
+    String output = gfsh.execute("query --query='this is not a valid query'");
+
+    assertThatOutputHasNoResult(output);
+    assertThatOutputHasMessage(output, "Query is invalid due for error : <Syntax error in query:");
+  }
+
+  @Test
+  public void queryGivesDescriptiveErrorMessageIfNoQueryIsSpecified() throws Exception {
+    String output = gfsh.execute("query");
+
+    assertThat(output)
+        .contains("You should specify option (--query, --file, --interactive) for this command");
+  }
+
+  private void assertThatOutputHasResult(String output) {
+    String resultPattern = "Result\\s+:\\s+" + "true";
+    assertThat(output).containsPattern(resultPattern);
+  }
+
+  private void assertThatOutputHasNoResult(String output) {
+    String resultPattern = "Result\\s+:\\s+" + "false";
+    assertThat(output).containsPattern(resultPattern);
+  }
+
+  private void assertThatOutputHasMessage(String output, String message) {
+    String patternToMatch = "Message\\s+:\\s+" + Pattern.quote(message);
+    assertThat(output).containsPattern(patternToMatch);
+  }
+
+  private String[] splitOnLineBreaks(String multilineString) {
+    return multilineString.split("[\\r\\n]+");
+  }
+
+  public static class Customer {
+    public String name;
+    public String address;
+
+    public Customer(String name, String address) {
+      this.name = name;
+      this.address = address;
+    }
+
+    public String toString() {
+      return name + address;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java
new file mode 100644
index 0000000..11da4b7
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class QueryCommandUnitTest {
+
+  @Test
+  public void query() throws Exception {
+    QueryCommand queryCommand = new QueryCommand();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java
new file mode 100644
index 0000000..0ce89c1
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.management.internal.cli.result;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.json.JSONArray;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class ResultBuilderTest {
+
+  @Test
+  public void messageExistsForString() throws Exception {
+    CommandResult result = (CommandResult) ResultBuilder.createInfoResult("test message");
+    assertThat(result.getContent().get("message")).isInstanceOf(JSONArray.class);
+    assertThat(result.getContent().get("message").toString()).isEqualTo("[\"test message\"]");
+  }
+
+  @Test
+  public void messageExistsForEmpty() throws Exception {
+    CommandResult result = (CommandResult) ResultBuilder.createInfoResult("");
+    assertThat(result.getContent().get("message")).isInstanceOf(JSONArray.class);
+    assertThat(result.getContent().get("message").toString()).isEqualTo("[\"\"]");
+
+  }
+
+  @Test
+  public void messageExistsForNull() throws Exception {
+    CommandResult result = (CommandResult) ResultBuilder.createInfoResult(null);
+    assertThat(result.getContent().get("message")).isInstanceOf(JSONArray.class);
+    assertThat(result.getContent().get("message").toString()).isEqualTo("[null]");
+
+  }
+
+  @Test
+  public void infoResultDataStructure() throws Exception {
+    InfoResultData result = ResultBuilder.createInfoResultData();
+    result.addLine("line 1");
+    result.addLine("line 2");
+    result.setFooter("Feet!");
+    result.setHeader("Header");
+    CommandResult cmdResult = (CommandResult) ResultBuilder.buildResult(result);
+
+    assertThat(cmdResult.getGfJsonObject().has("header")).isTrue();
+    assertThat(cmdResult.getGfJsonObject().has("content")).isTrue();
+    assertThat(cmdResult.getGfJsonObject().has("footer")).isTrue();
+
+    assertThat(cmdResult.getContent().has("message")).isTrue();
+
+    assertThat(cmdResult.getStatus()).isEqualTo(Result.Status.OK);
+  }
+
+  @Test
+  public void errorResultDataStructure() throws Exception {
+    ErrorResultData result = ResultBuilder.createErrorResultData();
+    result.addLine("line 1");
+    result.addLine("line 2");
+    result.setFooter("Feet!");
+    result.setHeader("Header");
+    CommandResult cmdResult = (CommandResult) ResultBuilder.buildResult(result);
+
+    assertThat(cmdResult.getGfJsonObject().has("header")).isTrue();
+    assertThat(cmdResult.getGfJsonObject().has("content")).isTrue();
+    assertThat(cmdResult.getGfJsonObject().has("footer")).isTrue();
+
+    assertThat(cmdResult.getContent().has("message")).isTrue();
+
+    assertThat(cmdResult.getStatus()).isEqualTo(Result.Status.ERROR);
+  }
+
+  @Test
+  public void tabularResultDataStructure() throws Exception {
+    TabularResultData result = ResultBuilder.createTabularResultData();
+    result.accumulate("column1", "value11");
+    result.accumulate("column1", "value12");
+    result.accumulate("column2", "value21");
+    result.accumulate("column2", "value22");
+
+    result.setFooter("Feet!");
+    result.setHeader("Header");
+    CommandResult cmdResult = (CommandResult) ResultBuilder.buildResult(result);
+
+    assertThat(cmdResult.getGfJsonObject().has("header")).isTrue();
+    assertThat(cmdResult.getGfJsonObject().has("content")).isTrue();
+    assertThat(cmdResult.getGfJsonObject().has("footer")).isTrue();
+
+    assertThat(cmdResult.getContent().has("column1")).isTrue();
+    assertThat(cmdResult.getContent().has("column2")).isTrue();
+
+    assertThat(cmdResult.getContent().getJSONArray("column1").toString()).contains("value11");
+    assertThat(cmdResult.getContent().getJSONArray("column1").toString()).contains("value12");
+    assertThat(cmdResult.getContent().getJSONArray("column2").toString()).contains("value21");
+    assertThat(cmdResult.getContent().getJSONArray("column2").toString()).contains("value22");
+  }
+
+  @Test
+  public void compositeResultDataStructure() throws Exception {
+    CompositeResultData result = ResultBuilder.createCompositeResultData();
+
+    result.setFooter("Feet!");
+    result.setHeader("Header");
+
+    assertThat(result.getGfJsonObject().has("header")).isTrue();
+    assertThat(result.getGfJsonObject().has("content")).isTrue();
+    assertThat(result.getGfJsonObject().has("footer")).isTrue();
+
+    // build up an example
+    result.addSection().addData("section 0 key", "section 0 value");
+    result.addSection().addTable().accumulate("table 1 column", "table 1 value");
+
+    result.addSection();
+
+  }
+}