[email protected] has uploaded a new change for review.

  https://asterix-gerrit.ics.uci.edu/3061

Change subject: [NO ISSUE][API] added parse-only request parameter
......................................................................

[NO ISSUE][API] added parse-only request parameter

  - user model changes: parse-only request parameter has been added
  - storage format changes: no
  - interface changes: no

  Details:
   - Support for new request parameter parse-only;
     Returns parsevals as result, which as statement-parameters
     as one of the key. The value of statement-parameters
     is all the named and positional parameters.

   - Augmented the test framework to support parse request

   - Added test cases

Change-Id: Iefa0edd1d9491335965fa9e6a1a9094d8070c83c
---
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
A 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ParseValuesJsonPrintUtils.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
M 
asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
M 
asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
A 
asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ParseOnlyTest.java
A 
asterixdb/asterix-app/src/test/resources/parseonly/queries_sqlpp/named_01.1.query.sqlpp
A asterixdb/asterix-app/src/test/resources/runtimets/only_parseonly.xml
A 
asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.1.parse.sqlpp
A 
asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.2.parse.sqlpp
A 
asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.1.adm
A 
asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.2.adm
A asterixdb/asterix-app/src/test/resources/runtimets/testsuite_parseonly.xml
A 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValueParser.java
A 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValues.java
M 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
A 
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/NamedParameterVisitor.java
19 files changed, 1,008 insertions(+), 70 deletions(-)


  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb 
refs/changes/61/3061/1

diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
index 9844900..9d923ec 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/AbstractQueryApiServlet.java
@@ -50,7 +50,8 @@
         ERRORS("errors"),
         METRICS("metrics"),
         PLANS("plans"),
-        WARNINGS("warnings");
+        WARNINGS("warnings"),
+        PARSEVALS("parsevals");
 
         private final String str;
 
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ParseValuesJsonPrintUtils.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ParseValuesJsonPrintUtils.java
new file mode 100644
index 0000000..9195c78
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ParseValuesJsonPrintUtils.java
@@ -0,0 +1,61 @@
+package org.apache.asterix.api.http.server;
+
+import org.apache.asterix.lang.sqlpp.parser.ParseValues;
+import org.apache.asterix.translator.SessionConfig;
+import org.apache.hyracks.util.JSONUtil;
+
+public class ParseValuesJsonPrintUtils {
+
+    private static final String EXTERNAL_VARIABLE_LBL = "statement-parameters";
+
+    public static String asJson(ParseValues parseValues) {
+        final StringBuilder output = new StringBuilder();
+        appendOutputPrefix(output);
+        appendNonNull(output, EXTERNAL_VARIABLE_LBL, 
parseValues.toStringDebug());
+        appendOutputPostfix(output);
+        return output.toString();
+    }
+
+    private static void appendNonNull(StringBuilder builder, String lbl, 
String value) {
+        //TODO: The format is string. Should we use JSON?
+
+        SessionConfig.PlanFormat format = SessionConfig.PlanFormat.STRING;
+
+        if (value != null) {
+            printFieldPrefix(builder, lbl);
+            switch (format) {
+                case JSON:
+                    //TODO: No support for JSON currently.
+                    break;
+                case STRING:
+                    JSONUtil.quoteAndEscape(builder, value);
+                    break;
+                default:
+                    throw new IllegalStateException("Unrecognized plan format: 
" + format);
+            }
+            printFieldPostfix(builder);
+        }
+    }
+
+    //TODO: Refactor to remove code duplication from 
ExecutionPlansJsonPrintUtil
+    private static void appendOutputPrefix(StringBuilder builder) {
+        builder.append("{");
+    }
+
+    private static void printFieldPrefix(StringBuilder builder, String lbl) {
+        builder.append("\"" + lbl + "\": ");
+    }
+
+    private static void printFieldPostfix(StringBuilder builder) {
+        builder.append(",");
+    }
+
+    private static void appendOutputPostfix(StringBuilder builder) {
+        // remove extra comma if needed
+        if (builder.length() > 1) {
+            builder.deleteCharAt(builder.length() - 1);
+        }
+        builder.append("}");
+    }
+
+}
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
index 16a2105..e0af3bd 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
@@ -44,6 +44,7 @@
     private String planFormat;
     private Map<String, JsonNode> statementParams;
     private boolean expressionTree;
+    private boolean parseOnly; //don't execute; simply check for syntax 
correctness and named parameters.
     private boolean rewrittenExpressionTree;
     private boolean logicalPlan;
     private boolean optimizedLogicalPlan;
@@ -171,6 +172,14 @@
         this.optimizedLogicalPlan = optimizedLogicalPlan;
     }
 
+    public void setParseOnly(boolean parseOnly) {
+        this.parseOnly = parseOnly;
+    }
+
+    public boolean isParseOnly() {
+        return parseOnly;
+    }
+
     public boolean isJob() {
         return job;
     }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index 765ba9c..41cc69d 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -28,11 +28,7 @@
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -49,20 +45,13 @@
 import org.apache.asterix.compiler.provider.ILangCompilationProvider;
 import org.apache.asterix.lang.aql.parser.TokenMgrError;
 import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.IParserFactory;
 import org.apache.asterix.lang.common.base.Statement;
 import org.apache.asterix.metadata.MetadataManager;
 import org.apache.asterix.om.base.IAObject;
-import org.apache.asterix.translator.ExecutionPlans;
-import org.apache.asterix.translator.ExecutionPlansJsonPrintUtil;
-import org.apache.asterix.translator.IRequestParameters;
-import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.*;
 import org.apache.asterix.translator.IStatementExecutor.ResultDelivery;
 import org.apache.asterix.translator.IStatementExecutor.Stats;
-import org.apache.asterix.translator.IStatementExecutorContext;
-import org.apache.asterix.translator.IStatementExecutorFactory;
-import org.apache.asterix.translator.ResultProperties;
-import org.apache.asterix.translator.SessionConfig;
-import org.apache.asterix.translator.SessionOutput;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.api.application.IServiceContext;
 import org.apache.hyracks.api.exceptions.HyracksException;
@@ -73,6 +62,9 @@
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+
+import org.apache.asterix.lang.sqlpp.parser.ParseValueParser;
+import org.apache.asterix.lang.sqlpp.parser.ParseValues;
 
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
@@ -153,6 +145,7 @@
         REWRITTEN_EXPRESSION_TREE("rewritten-expression-tree"),
         LOGICAL_PLAN("logical-plan"),
         OPTIMIZED_LOGICAL_PLAN("optimized-logical-plan"),
+        PARSE_ONLY("parse-only"),
         JOB("job"),
         SIGNATURE("signature"),
         MULTI_STATEMENT("multi-statement");
@@ -436,6 +429,7 @@
                 param.setLogicalPlan(getOptBoolean(jsonRequest, 
Parameter.LOGICAL_PLAN.str(), false));
                 param.setOptimizedLogicalPlan(
                         getOptBoolean(jsonRequest, 
Parameter.OPTIMIZED_LOGICAL_PLAN.str(), false));
+                param.setParseOnly(getOptBoolean(jsonRequest, 
Parameter.PARSE_ONLY.str(), false));
                 param.setJob(getOptBoolean(jsonRequest, Parameter.JOB.str(), 
false));
                 param.setSignature(getOptBoolean(jsonRequest, 
Parameter.SIGNATURE.str(), true));
                 param.setStatementParams(
@@ -525,60 +519,131 @@
         HttpUtil.setContentType(response, 
HttpUtil.ContentType.APPLICATION_JSON, HttpUtil.Encoding.UTF8);
 
         Stats stats = new Stats();
-        RequestExecutionState execution = new RequestExecutionState();
 
         // buffer the output until we are ready to set the status of the 
response message correctly
         sessionOutput.hold();
         sessionOutput.out().print("{\n");
         printRequestId(sessionOutput.out());
         printClientContextID(sessionOutput.out(), param);
-        printSignature(sessionOutput.out(), param);
+        if (!param.isParseOnly()) {
+            printSignature(sessionOutput.out(), param);
+        }
         printType(sessionOutput.out(), sessionConfig);
         long errorCount = 1; // so far we just return 1 error
         List<ExecutionWarning> warnings = Collections.emptyList(); // we don't 
have any warnings yet
-        try {
-            if (param.getStatement() == null || 
param.getStatement().isEmpty()) {
-                throw new 
RuntimeDataException(ErrorCode.NO_STATEMENT_PROVIDED);
-            }
-            String statementsText = param.getStatement() + ";";
-            Map<String, String> optionalParams = null;
-            if (optionalParamProvider != null) {
-                optionalParams = optionalParamProvider.apply(request);
-            }
-            Map<String, byte[]> statementParams = 
org.apache.asterix.app.translator.RequestParameters
-                    .serializeParameterValues(param.getStatementParams());
-            // CORS
-            if (request.getHeader("Origin") != null) {
-                response.setHeader("Access-Control-Allow-Origin", 
request.getHeader("Origin"));
-            }
-            response.setHeader("Access-Control-Allow-Headers", "Origin, 
X-Requested-With, Content-Type, Accept");
-            response.setStatus(execution.getHttpStatus());
-            executeStatement(statementsText, sessionOutput, resultProperties, 
stats, param, execution, optionalParams,
-                    statementParams);
-            if (ResultDelivery.IMMEDIATE == delivery || 
ResultDelivery.DEFERRED == delivery) {
+
+        if (param.getStatement() == null || param.getStatement().isEmpty()) {
+            throw new RuntimeDataException(ErrorCode.NO_STATEMENT_PROVIDED);
+        } //TODO: moved this out of try-catch block.
+
+        String statementsText = param.getStatement() + ";";
+
+        RequestExecutionState execution = new RequestExecutionState();
+        if (param.isParseOnly()) {
+            try {
+                ParseValues parseValues = parseStatement(statementsText); //is 
cousin of executeStatement
+                // CORS
+                if (request.getHeader("Origin") != null) {
+                    response.setHeader("Access-Control-Allow-Origin", 
request.getHeader("Origin"));
+                }
+                response.setHeader("Access-Control-Allow-Headers", "Origin, 
X-Requested-With, Content-Type, Accept");
+
+                response.setStatus(HttpResponseStatus.OK);
+                if (ResultDelivery.IMMEDIATE == delivery || 
ResultDelivery.DEFERRED == delivery) {
+                    ResultUtil.printStatus(sessionOutput, 
execution.getResultStatus());
+                }
+                if (!warnings.isEmpty()) {
+                    printWarnings(sessionOutput.out(), warnings);
+                }
+                errorCount = 0;
+                printParseValues(sessionOutput, parseValues);
+
+            } catch (Exception | TokenMgrError | 
org.apache.asterix.aqlplus.parser.TokenMgrError e) {
+                //TODO: figure out what to do with compilation error
+                //TODO: invoke the already backed exception handling
+                if (LOGGER.isDebugEnabled()) {
+
+                    LOGGER.debug("handleException: {}: {}", e.getMessage(), 
param, e);
+                } else {
+                    LOGGER.info("handleException: {}: {}", e.getMessage(), 
param);
+                    //sessionOutput.out()
+                    //       .print(e.getMessage() + ";" + 
e.getLocalizedMessage() + ";" + e.getSourceLocation());
+                }
+                handleExecuteStatementException(e, execution, param); //hope 
this is generic enough to do its thing.
+                response.setStatus(execution.getHttpStatus());
+                printError(sessionOutput.out(), e);
                 ResultUtil.printStatus(sessionOutput, 
execution.getResultStatus());
+                if (!warnings.isEmpty()) {
+                    printWarnings(sessionOutput.out(), warnings);
+                }
+
+            } finally {
+                // make sure that we stop buffering and return the result to 
the http response
+                sessionOutput.release();
+
             }
-            if (!warnings.isEmpty()) {
-                printWarnings(sessionOutput.out(), warnings);
+            //printMetrics(sessionOutput.out(), System.nanoTime() - 
elapsedStart, execution.duration(), stats.getCount(),
+            //        stats.getSize(), stats.getProcessedObjects(), 
errorCount, warnings.size());
+
+        } else {
+
+            try {
+                Map<String, String> optionalParams = null;
+                if (optionalParamProvider != null) {
+                    optionalParams = optionalParamProvider.apply(request);
+                }
+                Map<String, byte[]> statementParams = 
org.apache.asterix.app.translator.RequestParameters
+                        .serializeParameterValues(param.getStatementParams());
+                // CORS
+                if (request.getHeader("Origin") != null) {
+                    response.setHeader("Access-Control-Allow-Origin", 
request.getHeader("Origin"));
+                }
+                response.setHeader("Access-Control-Allow-Headers", "Origin, 
X-Requested-With, Content-Type, Accept");
+                response.setStatus(execution.getHttpStatus());
+                executeStatement(statementsText, sessionOutput, 
resultProperties, stats, param, execution,
+                        optionalParams, statementParams);
+                if (ResultDelivery.IMMEDIATE == delivery || 
ResultDelivery.DEFERRED == delivery) {
+                    ResultUtil.printStatus(sessionOutput, 
execution.getResultStatus());
+                }
+                if (!warnings.isEmpty()) {
+                    printWarnings(sessionOutput.out(), warnings);
+                }
+                errorCount = 0;
+
+            } catch (Exception | TokenMgrError | 
org.apache.asterix.aqlplus.parser.TokenMgrError e) {
+                handleExecuteStatementException(e, execution, param);
+                response.setStatus(execution.getHttpStatus());
+                printError(sessionOutput.out(), e);
+                ResultUtil.printStatus(sessionOutput, 
execution.getResultStatus());
+            } finally {
+                // make sure that we stop buffering and return the result to 
the http response
+                sessionOutput.release();
+                execution.finish();
             }
-            errorCount = 0;
-        } catch (Exception | TokenMgrError | 
org.apache.asterix.aqlplus.parser.TokenMgrError e) {
-            handleExecuteStatementException(e, execution, param);
-            response.setStatus(execution.getHttpStatus());
-            printError(sessionOutput.out(), e);
-            ResultUtil.printStatus(sessionOutput, execution.getResultStatus());
-        } finally {
-            // make sure that we stop buffering and return the result to the 
http response
-            sessionOutput.release();
-            execution.finish();
+            printMetrics(sessionOutput.out(), System.nanoTime() - 
elapsedStart, execution.duration(), stats.getCount(),
+                    stats.getSize(), stats.getProcessedObjects(), errorCount, 
warnings.size());
+
         }
-        printMetrics(sessionOutput.out(), System.nanoTime() - elapsedStart, 
execution.duration(), stats.getCount(),
-                stats.getSize(), stats.getProcessedObjects(), errorCount, 
warnings.size());
         sessionOutput.out().print("}\n");
         sessionOutput.out().flush();
         if (sessionOutput.out().checkError()) {
             LOGGER.warn("Error flushing output writer");
         }
+    }
+
+    protected ParseValues parseStatement(String statementsText) throws 
Exception {
+        //assert (param.isMultiStatement() == false);
+        try {
+            //IParser parser = 
compilationProvider.getParserFactory().createParser(statementsText);
+            IParserFactory factory = compilationProvider.getParserFactory();
+            ParseValueParser parser = new ParseValueParser(factory, 
statementsText);
+            parser.parse();
+            return parser.getParseValues();
+
+        } catch (Exception ce) {
+            throw ce;
+        }
+
     }
 
     protected void executeStatement(String statementsText, SessionOutput 
sessionOutput,
@@ -651,6 +716,19 @@
         ResultUtil.printWarnings(pw, warnings);
     }
 
+    protected void printParseValues(SessionOutput output, ParseValues 
parseValues) {
+        final PrintWriter pw = output.out();
+        pw.print("\t\"");
+        pw.print(ResultFields.PARSEVALS.str());
+        pw.print("\":");
+
+        pw.print(ParseValuesJsonPrintUtils.asJson(parseValues));
+
+        //pw.print(",\n"); //TODO: comma or no comma
+        pw.print("\n"); //TODO: comma or no comma
+
+    }
+
     protected void printExecutionPlans(SessionOutput output, ExecutionPlans 
executionPlans) {
         final PrintWriter pw = output.out();
         pw.print("\t\"");
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index 3638e55..6845641 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -251,6 +251,10 @@
         return sessionOutput;
     }
 
+    public List<Statement> getStatements() {
+        return this.statements;
+    }
+
     protected List<FunctionDecl> getDeclaredFunctions(List<Statement> 
statements) {
         List<FunctionDecl> functionDecls = new ArrayList<>();
         for (Statement st : statements) {
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
index 412cf03..e71c2d9 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java
@@ -55,7 +55,8 @@
         TYPE("type"),
         ERRORS("errors"),
         PLANS("plans"),
-        WARNINGS("warnings");
+        WARNINGS("warnings"),
+        PARSEVALS("parsevals");
 
         private static final Map<String, ResultField> fields = new HashMap<>();
 
@@ -90,6 +91,11 @@
     public static InputStream extractMetrics(InputStream resultStream) throws 
Exception {
         return extract(resultStream, EnumSet.of(ResultField.METRICS));
     }
+
+    public static InputStream extractParseResults(InputStream resultStream) 
throws Exception {
+        return extract(resultStream, EnumSet.of(ResultField.PARSEVALS));
+    }
+
 
     public static String extractHandle(InputStream resultStream) throws 
Exception {
         String result = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
@@ -165,6 +171,7 @@
                 case STATUS:
                 case TYPE:
                 case PLANS:
+                case PARSEVALS:
                 case WARNINGS:
                     
resultBuilder.append(OBJECT_MAPPER.writeValueAsString(fieldValue));
                     break;
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index 9f4f248..ed989e8 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -37,16 +37,7 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -153,6 +144,7 @@
     public static final String DELIVERY_IMMEDIATE = "immediate";
     public static final String DIAGNOSE = "diagnose";
     private static final String METRICS_QUERY_TYPE = "metrics";
+    private static final String PARSE_QUERY_TYPE = "parse";
 
     private static final HashMap<Integer, ITestServer> runningTestServers = 
new HashMap<>();
     private static Map<String, InetSocketAddress> ncEndPoints;
@@ -889,6 +881,7 @@
                 break;
             case "query":
             case "async":
+            case "parse":
             case "deferred":
             case "metrics":
                 // isDmlRecoveryTest: insert Crash and Recovery
@@ -1219,8 +1212,17 @@
         if (DELIVERY_IMMEDIATE.equals(delivery)) {
             resultStream =
                     executeQueryService(statement, fmt, uri, params, 
isJsonEncoded, null, isCancellable(reqType));
-            resultStream = METRICS_QUERY_TYPE.equals(reqType) ? 
ResultExtractor.extractMetrics(resultStream)
-                    : ResultExtractor.extract(resultStream);
+            switch(reqType){
+                case METRICS_QUERY_TYPE:
+                    resultStream = 
ResultExtractor.extractMetrics(resultStream);
+                    break;
+                case PARSE_QUERY_TYPE:
+                    resultStream = 
ResultExtractor.extractParseResults(resultStream);
+                    break;
+                default:
+                    resultStream = ResultExtractor.extract(resultStream);
+                    break;
+            }
         } else {
             String handleVar = getHandleVariable(statement);
             resultStream = executeQueryService(statement, fmt, uri,
@@ -1490,7 +1492,13 @@
         while (m.find()) {
             final Parameter param = new Parameter();
             String name = m.group("name");
-            param.setName(name);
+            if (name.equals("parse")) {
+                String newname= "parse-only";
+                param.setName(newname);
+            }
+            else {
+                param.setName(name);
+            }
             String value = m.group("value");
             param.setValue(value);
             String type = m.group("type");
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ParseOnlyTest.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ParseOnlyTest.java
new file mode 100644
index 0000000..73f98a0
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ParseOnlyTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.asterix.test.runtime;
+
+
+import java.util.Collection;
+
+
+import org.apache.asterix.test.common.TestExecutor;
+
+
+import org.apache.asterix.testframework.context.TestCaseContext;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+
+//TODO: eventually dummy test should come here
+
+@RunWith(Parameterized.class)
+public class ParseOnlyTest {
+
+
+    protected static final String TEST_CONFIG_FILE_NAME = 
"src/main/resources/cc.conf";
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        LangExecutionUtil.setUp(TEST_CONFIG_FILE_NAME, new TestExecutor());
+    }
+
+    @AfterClass
+    public static void tearDown() throws Exception {
+        LangExecutionUtil.tearDown();
+    }
+
+    @Parameters(name = "ParseOnlyTest {index}: {0}")
+    public static Collection<Object[]> tests() throws Exception {
+        return LangExecutionUtil.tests("only.xml", "testsuite_parseonly.xml");
+    }
+
+    protected TestCaseContext tcCtx;
+
+    public ParseOnlyTest(TestCaseContext tcCtx) {
+        this.tcCtx = tcCtx;
+    }
+
+    @Test
+    public void test() throws Exception {
+        LangExecutionUtil.test(tcCtx);
+    }
+
+}
diff --git 
a/asterixdb/asterix-app/src/test/resources/parseonly/queries_sqlpp/named_01.1.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/parseonly/queries_sqlpp/named_01.1.query.sqlpp
new file mode 100644
index 0000000..a8c05a7
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/parseonly/queries_sqlpp/named_01.1.query.sqlpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description  : Test named statement parameters with json encoded request
+ * Expected Res : Success
+ * Date         : Jun 2018
+ */
+
+-- param parse:string=true
+
+// requesttype=application/json
+
+// param $p_null:json=null
+// param $p_bool:json=true
+// param $p_int:json=42
+// param $p_dec:json=42.5
+// param $p_dbl:json=42.5e2
+// param $p_str:json="hello"
+// param $p_arr:json=["99",100,{"a":null},null,true]
+// param $p_obj:json={"a":[1,2,3]}
+
+{
+  "t1": {
+    "p_null": $p_null,
+    "p_bool": $p_bool,
+    "p_int": $p_int,
+    "p_dec": $p_dec,
+    "p_dbl": $p_dbl,
+    "p_str": $p_str,
+    "p_arr": $p_arr,
+    "p_obj": $p_obj
+  },
+
+  "t2": {
+    "p_null_type": $p_null is null,
+    "p_bool_type": is_boolean($p_bool),
+    "p_int_type": is_number($p_int),
+    "p_dec_type": is_number($p_dec),
+    "p_dbl_type": is_number($p_dbl),
+    "p_str_type": is_string($p_str),
+    "p_arr_type": is_array($p_arr),
+    "p_obj_type": is_object($p_obj)
+  },
+
+  "t3": [ $p_null, $p_bool, $p_int, $p_dec, $p_dbl, $p_str, $p_arr, $p_obj ]
+}
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/only_parseonly.xml 
b/asterixdb/asterix-app/src/test/resources/runtimets/only_parseonly.xml
new file mode 100644
index 0000000..e1b94d7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/only_parseonly.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ! 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.
+ !-->
+<test-suite xmlns="urn:xml.testframework.asterix.apache.org" 
ResultOffsetPath="results" QueryOffsetPath="queries" 
QueryFileExtension=".sqlpp">
+  <test-group name="parseonly">
+  </test-group>
+</test-suite>
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.1.parse.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.1.parse.sqlpp
new file mode 100644
index 0000000..cf43e64
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.1.parse.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description  : Test named statement parameters with json encoded request
+ * Expected Res : Success
+ * Date         : Jun 2018
+ */
+
+-- param parse:string=true
+
+select count(*) from ChirpUsers where name=$qname;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.2.parse.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.2.parse.sqlpp
new file mode 100644
index 0000000..a8c05a7
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/named_01.2.parse.sqlpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description  : Test named statement parameters with json encoded request
+ * Expected Res : Success
+ * Date         : Jun 2018
+ */
+
+-- param parse:string=true
+
+// requesttype=application/json
+
+// param $p_null:json=null
+// param $p_bool:json=true
+// param $p_int:json=42
+// param $p_dec:json=42.5
+// param $p_dbl:json=42.5e2
+// param $p_str:json="hello"
+// param $p_arr:json=["99",100,{"a":null},null,true]
+// param $p_obj:json={"a":[1,2,3]}
+
+{
+  "t1": {
+    "p_null": $p_null,
+    "p_bool": $p_bool,
+    "p_int": $p_int,
+    "p_dec": $p_dec,
+    "p_dbl": $p_dbl,
+    "p_str": $p_str,
+    "p_arr": $p_arr,
+    "p_obj": $p_obj
+  },
+
+  "t2": {
+    "p_null_type": $p_null is null,
+    "p_bool_type": is_boolean($p_bool),
+    "p_int_type": is_number($p_int),
+    "p_dec_type": is_number($p_dec),
+    "p_dbl_type": is_number($p_dbl),
+    "p_str_type": is_string($p_str),
+    "p_arr_type": is_array($p_arr),
+    "p_obj_type": is_object($p_obj)
+  },
+
+  "t3": [ $p_null, $p_bool, $p_int, $p_dec, $p_dbl, $p_str, $p_arr, $p_obj ]
+}
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.1.adm
new file mode 100644
index 0000000..4d06619
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.1.adm
@@ -0,0 +1 @@
+{"externalVariables":"[?qname,]"}
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.2.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.2.adm
new file mode 100644
index 0000000..decacc0
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/named_01.2.adm
@@ -0,0 +1 @@
+{"externalVariables":"[?p_obj,?p_null,?p_bool,?p_dbl,?p_int,?p_str,?p_arr,?p_dec,]"}
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_parseonly.xml 
b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_parseonly.xml
new file mode 100644
index 0000000..a6d945f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_parseonly.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ! Copyright 2017-2018 Couchbase, Inc.
+ !-->
+<test-suite xmlns="urn:xml.testframework.asterix.apache.org" 
ResultOffsetPath="results" QueryOffsetPath="queries"
+            QueryFileExtension=".sqlpp">
+    <test-group name="parseonly">
+        <test-case FilePath="parseonly">
+            <compilation-unit name="001">
+                <output-dir compare="Text">001</output-dir>
+            </compilation-unit>
+        </test-case>
+    </test-group>
+</test-suite>
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValueParser.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValueParser.java
new file mode 100644
index 0000000..14770b4
--- /dev/null
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValueParser.java
@@ -0,0 +1,70 @@
+/*
+ * 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.asterix.lang.sqlpp.parser;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+
+import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.IParserFactory;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.sqlpp.visitor.NamedParameterVisitor;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ParseValueParser {
+    private final IParserFactory parserFactory;
+    private final ParseValues parseValues;
+    private final Set<VariableExpr> extVars;
+    private final String statementsText;
+    private IParser parser;
+
+    public ParseValueParser(IParserFactory parserFactory, String 
statementsText) {
+        this.statementsText = statementsText;
+        this.parserFactory = parserFactory;
+        this.extVars = new HashSet<>();
+        this.parseValues = new ParseValues();
+
+    }
+
+    public List<Statement> parse() throws CompilationException {
+        parser = parserFactory.createParser(statementsText);
+        List<Statement> statements = parser.parse();
+        NamedParameterVisitor erv = new NamedParameterVisitor();
+        for (Statement st : statements) {
+            st.accept(erv, extVars);
+        }
+        parseValues.setExternalVariables(extVars);
+        return statements;
+
+    }
+
+    public ParseValues getParseValues() {
+        return parseValues;
+    }
+
+    public String toStringDebug() {
+        return parseValues.toStringDebug();
+
+    }
+
+}
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValues.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValues.java
new file mode 100644
index 0000000..f71359e
--- /dev/null
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseValues.java
@@ -0,0 +1,51 @@
+/*
+ * 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.asterix.lang.sqlpp.parser;
+
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+
+import java.io.Serializable;
+import java.util.Set;
+
+public class ParseValues implements Serializable {
+    private Set<VariableExpr> externalVariables;
+    private String extVarsSerialized;
+    public Set<VariableExpr> getExternalVariables() {
+        return externalVariables;
+    }
+
+    public void setExternalVariables(Set<VariableExpr> extVars) {
+        this.externalVariables = extVars;
+    }
+
+    public String toStringDebug() {
+        extVarsSerialized = "[";
+
+        for (VariableExpr extVarRef : externalVariables) {
+            extVarsSerialized += 
SqlppVariableUtil.toUserDefinedName(extVarRef.getVar().getValue()) + ",";
+        }
+        extVarsSerialized += "]";
+
+        return extVarsSerialized;
+
+    }
+
+}
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
index 14dfaeb..c2b3e45 100644
--- 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppVariableUtil.java
@@ -18,11 +18,7 @@
  */
 package org.apache.asterix.lang.sqlpp.util;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
+import java.util.*;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.lang.common.base.ILangExpression;
diff --git 
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/NamedParameterVisitor.java
 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/NamedParameterVisitor.java
new file mode 100644
index 0000000..20d0748
--- /dev/null
+++ 
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/NamedParameterVisitor.java
@@ -0,0 +1,388 @@
+/*
+ * 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.asterix.lang.sqlpp.visitor;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.clause.*;
+import org.apache.asterix.lang.common.expression.*;
+import org.apache.asterix.lang.common.statement.FunctionDecl;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.common.struct.Identifier;
+import org.apache.asterix.lang.common.struct.QuantifiedPair;
+import org.apache.asterix.lang.sqlpp.clause.*;
+import org.apache.asterix.lang.sqlpp.expression.CaseExpression;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
+import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import 
org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import static 
org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil.isExternalVariableReference;
+
+public class NamedParameterVisitor extends 
AbstractSqlppQueryExpressionVisitor<Void, Collection<VariableExpr>> {
+    @Override
+    public Void visit(FromClause fromClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        for (FromTerm fromTerm : fromClause.getFromTerms()) {
+            Collection<VariableExpr> fromTermExternalVars = new HashSet<>();
+            fromTerm.accept(this, fromTermExternalVars);
+            externalVars.addAll(fromTermExternalVars);
+            //TODO does binding vars need to be removed? probably see: 
FreeVariableVisitor.java:visit(FromClause..)
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(FromTerm fromTerm, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        // Visit the left expression of a from term.
+        fromTerm.getLeftExpression().accept(this, externalVars);
+        //TODO should binding variables in correlated clause needs to be 
removed? see FreeVariableVisitor::visit(FromTerm)
+        return null;
+    }
+
+    @Override
+    public Void visit(JoinClause joinClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        visitJoinAndNest(joinClause, joinClause.getConditionExpression(), 
externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(NestClause nestClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        visitJoinAndNest(nestClause, nestClause.getConditionExpression(), 
externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(UnnestClause unnestClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        unnestClause.getRightExpression().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(Projection projection, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        if (!projection.star()) {
+            projection.getExpression().accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectBlock selectBlock, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        Collection<VariableExpr> selectexternalVars = new HashSet<>();
+        Collection<VariableExpr> fromexternalVars = new HashSet<>();
+        Collection<VariableExpr> letsexternalVars = new HashSet<>();
+        Collection<VariableExpr> whereexternalVars = new HashSet<>();
+        Collection<VariableExpr> gbyexternalVars = new HashSet<>();
+        Collection<VariableExpr> gbyLetsexternalVars = new HashSet<>();
+
+        //TODO what about binding variables
+
+        selectBlock.getSelectClause().accept(this, selectexternalVars);
+
+        if (selectBlock.hasFromClause()) {
+            selectBlock.getFromClause().accept(this, fromexternalVars);
+        }
+        if (selectBlock.hasLetClauses()) {
+            visitLetClauses(selectBlock.getLetList(), letsexternalVars);
+
+        }
+        if (selectBlock.hasWhereClause()) {
+            selectBlock.getWhereClause().accept(this, whereexternalVars);
+        }
+        if (selectBlock.hasGroupbyClause()) {
+            selectBlock.getGroupbyClause().accept(this, gbyexternalVars);
+
+            if (selectBlock.hasLetClausesAfterGroupby()) {
+                visitLetClauses(selectBlock.getLetListAfterGroupby(), 
gbyLetsexternalVars);
+            }
+            if (selectBlock.hasHavingClause()) {
+                selectBlock.getHavingClause().accept(this, selectexternalVars);
+            }
+        }
+
+        // Adds all free vars.
+        externalVars.addAll(selectexternalVars);
+        externalVars.addAll(fromexternalVars);
+        externalVars.addAll(letsexternalVars);
+        externalVars.addAll(whereexternalVars);
+        externalVars.addAll(gbyexternalVars);
+        externalVars.addAll(gbyLetsexternalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectClause selectClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        if (selectClause.selectElement()) {
+            selectClause.getSelectElement().accept(this, externalVars);
+        }
+        if (selectClause.selectRegular()) {
+            selectClause.getSelectRegular().accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectElement selectElement, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        selectElement.getExpression().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectRegular selectRegular, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        for (Projection projection : selectRegular.getProjections()) {
+            projection.accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectSetOperation selectSetOperation, 
Collection<VariableExpr> externalVars)
+            throws CompilationException {
+        selectSetOperation.getLeftInput().accept(this, externalVars);
+        for (SetOperationRight right : selectSetOperation.getRightInputs()) {
+            right.getSetOperationRightInput().accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(HavingClause havingClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        havingClause.getFilterExpression().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(Query q, Collection<VariableExpr> externalVars) throws 
CompilationException {
+        q.getBody().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(FunctionDecl fd, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        fd.getFuncBody().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(WhereClause whereClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        whereClause.getWhereExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(OrderbyClause oc, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        visit(oc.getOrderbyList(), externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(GroupbyClause gc, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        // Puts all group-by variables into the symbol set of the new scope.
+        for (GbyVariableExpressionPair gbyVarExpr : gc.getGbyPairList()) {
+            gbyVarExpr.getExpr().accept(this, externalVars);
+        }
+        for (GbyVariableExpressionPair decorVarExpr : gc.getDecorPairList()) {
+            decorVarExpr.getExpr().accept(this, externalVars);
+        }
+        if (gc.hasGroupFieldList()) {
+            for (Pair<Expression, Identifier> groupField : 
gc.getGroupFieldList()) {
+                groupField.first.accept(this, externalVars);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(LimitClause limitClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        limitClause.getLimitExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(LetClause letClause, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        letClause.getBindingExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(SelectExpression selectExpression, 
Collection<VariableExpr> externalVars)
+            throws CompilationException {
+        Collection<VariableExpr> letsexternalVars = new HashSet<>();
+        Collection<VariableExpr> selectexternalVars = new HashSet<>();
+        visitLetClauses(selectExpression.getLetList(), letsexternalVars);
+
+        // visit order by
+        if (selectExpression.hasOrderby()) {
+            for (Expression orderExpr : 
selectExpression.getOrderbyClause().getOrderbyList()) {
+                orderExpr.accept(this, selectexternalVars);
+            }
+        }
+
+        // visit limit
+        if (selectExpression.hasLimit()) {
+            selectExpression.getLimitClause().accept(this, selectexternalVars);
+        }
+
+        // visit the main select
+        selectExpression.getSelectSetOperation().accept(this, 
selectexternalVars);
+
+        //TODO Does bindingVars need to be handled?
+        externalVars.addAll(letsexternalVars);
+        externalVars.addAll(selectexternalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(LiteralExpr l, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        return null;
+    }
+
+    @Override
+    public Void visit(ListConstructor lc, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        visit(lc.getExprList(), externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(RecordConstructor rc, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        for (FieldBinding binding : rc.getFbList()) {
+            binding.getLeftExpr().accept(this, externalVars);
+            binding.getRightExpr().accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(OperatorExpr operatorExpr, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        visit(operatorExpr.getExprList(), externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(IfExpr ifExpr, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        ifExpr.getCondExpr().accept(this, externalVars);
+        ifExpr.getThenExpr().accept(this, externalVars);
+        ifExpr.getElseExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(QuantifiedExpression qe, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        for (QuantifiedPair pair : qe.getQuantifiedList()) {
+            pair.getExpr().accept(this, externalVars);
+        }
+        qe.getSatisfiesExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(CallExpr callExpr, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        for (Expression expr : callExpr.getExprList()) {
+            expr.accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(VariableExpr varExpr, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        if (isExternalVariableReference(varExpr)) {
+            System.out.println(
+                    "External variable found" + 
SqlppVariableUtil.toUserDefinedName(varExpr.getVar().getValue()) + " "
+                            + varExpr.getVar().getValue());
+            externalVars.add(varExpr);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(UnaryExpr u, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        u.getExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(FieldAccessor fa, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        fa.getExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(IndexAccessor ia, Collection<VariableExpr> externalVars) 
throws CompilationException {
+        ia.getExpr().accept(this, externalVars);
+        if (ia.getIndexExpr() != null) {
+            ia.getIndexExpr().accept(this, externalVars);
+        }
+        return null;
+    }
+
+    @Override
+    public Void visit(CaseExpression caseExpr, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        caseExpr.getConditionExpr().accept(this, externalVars);
+        visit(caseExpr.getWhenExprs(), externalVars);
+        visit(caseExpr.getThenExprs(), externalVars);
+        caseExpr.getElseExpr().accept(this, externalVars);
+        return null;
+    }
+
+    @Override
+    public Void visit(WindowExpression winExpr, Collection<VariableExpr> 
externalVars) throws CompilationException {
+        winExpr.getExpr().accept(this, externalVars);
+        if (winExpr.hasPartitionList()) {
+            visit(winExpr.getPartitionList(), externalVars);
+        }
+        visit(winExpr.getOrderbyList(), externalVars);
+        return null;
+    }
+
+    private void visitLetClauses(List<LetClause> letClauses, 
Collection<VariableExpr> externalVars)
+            throws CompilationException {
+        if (letClauses == null || letClauses.isEmpty()) {
+            return;
+        }
+        Collection<VariableExpr> bindingVars = new HashSet<>();
+        for (LetClause letClause : letClauses) {
+            Collection<VariableExpr> letExternalVars = new HashSet<>();
+            letClause.accept(this, letExternalVars);
+            externalVars.addAll(letExternalVars);
+            //TODO need to deal with binding vars, see 
FreeVariableVisitor::visitLetClause
+        }
+    }
+
+    private void visitJoinAndNest(AbstractBinaryCorrelateClause clause, 
Expression condition,
+            Collection<VariableExpr> externalVars) throws CompilationException 
{
+        clause.getRightExpression().accept(this, externalVars);
+        Collection<VariableExpr> conditionExternalVars = new HashSet<>();
+        condition.accept(this, externalVars);
+
+        //TODO see FreeVariableVisitor::visitJoinAndNest
+
+    }
+
+    private void visit(List<Expression> exprs, Collection<VariableExpr> arg) 
throws CompilationException {
+        for (Expression expr : exprs) {
+            expr.accept(this, arg);
+        }
+    }
+
+}

-- 
To view, visit https://asterix-gerrit.ics.uci.edu/3061
To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Iefa0edd1d9491335965fa9e6a1a9094d8070c83c
Gerrit-PatchSet: 1
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Owner: [email protected]

Reply via email to