[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 named parse-only;
Returns as result an object with single key-value pair.
The key is statement-parameters and the is a sorted array
containing positional and named free parameters.
- Added test cases
Change-Id: Idd2f461c22b05a5fcaa50a6e4f9b7dcd91acc184
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3085
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Till Westmann <tillw@apache.org>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
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 7e563e17..fce9365 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
@@ -33,23 +33,29 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.apache.asterix.algebra.base.ILangExtension;
+import org.apache.asterix.app.translator.QueryTranslator;
import org.apache.asterix.common.api.Duration;
import org.apache.asterix.common.api.IApplicationContext;
import org.apache.asterix.common.api.IClusterManagementWork;
import org.apache.asterix.common.config.GlobalConfig;
import org.apache.asterix.common.context.IStorageComponentProvider;
import org.apache.asterix.common.dataflow.ICcApplicationContext;
+import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.common.exceptions.RuntimeDataException;
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.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.statement.Query;
import org.apache.asterix.metadata.MetadataManager;
import org.apache.asterix.om.base.IAObject;
import org.apache.asterix.translator.ExecutionPlans;
@@ -141,6 +147,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");
@@ -411,6 +418,7 @@
param.setExpressionTree(getOptBoolean(jsonRequest, Parameter.EXPRESSION_TREE.str(), false));
param.setRewrittenExpressionTree(getOptBoolean(jsonRequest, Parameter.REWRITTEN_EXPRESSION_TREE.str(), false));
param.setLogicalPlan(getOptBoolean(jsonRequest, Parameter.LOGICAL_PLAN.str(), false));
+ param.setParseOnly(getOptBoolean(jsonRequest, Parameter.PARSE_ONLY.str(), false));
param.setOptimizedLogicalPlan(getOptBoolean(jsonRequest, Parameter.OPTIMIZED_LOGICAL_PLAN.str(), false));
param.setJob(getOptBoolean(jsonRequest, Parameter.JOB.str(), false));
param.setSignature(getOptBoolean(jsonRequest, Parameter.SIGNATURE.str(), true));
@@ -435,6 +443,7 @@
param.setTimeout(request.getParameter(Parameter.TIMEOUT.str()));
param.setMaxResultReads(request.getParameter(Parameter.MAX_RESULT_READS.str()));
param.setPlanFormat(request.getParameter(Parameter.PLAN_FORMAT.str()));
+ param.setParseOnly(Boolean.parseBoolean(request.getParameter(Parameter.PARSE_ONLY.str())));
final String multiStatementParam = request.getParameter(Parameter.MULTI_STATEMENT.str());
param.setMultiStatement(multiStatementParam == null || Boolean.parseBoolean(multiStatementParam));
try {
@@ -445,6 +454,14 @@
}
}
+ private void setAccessControlHeaders(IServletRequest request, IServletResponse response) throws IOException {
+ //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");
+ }
+
private static ResultDelivery parseResultDelivery(String mode) {
if ("async".equals(mode)) {
return ResultDelivery.ASYNC;
@@ -506,29 +523,36 @@
LOGGER.info("handleRequest: {}", param);
ResultDelivery delivery = parseResultDelivery(param.getMode());
setSessionConfig(sessionOutput, param, delivery);
- ResultProperties resultProperties = param.getMaxResultReads() == null ? new ResultProperties(delivery)
+ final ResultProperties resultProperties = param.getMaxResultReads() == null ? new ResultProperties(delivery)
: new ResultProperties(delivery, Long.parseLong(param.getMaxResultReads()));
printAdditionalResultFields(sessionOutput.out());
printRequestId(sessionOutput.out());
printClientContextID(sessionOutput.out(), param);
- printSignature(sessionOutput.out(), param);
+ if (!param.isParseOnly()) {
+ printSignature(sessionOutput.out(), param);
+ }
printType(sessionOutput.out(), sessionOutput.config());
if (param.getStatement() == null || param.getStatement().isEmpty()) {
throw new RuntimeDataException(ErrorCode.NO_STATEMENT_PROVIDED);
}
String statementsText = param.getStatement() + ";";
- 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.isParseOnly()) {
+ ResultUtil.ParseOnlyResult parseOnlyResult = parseStatement(statementsText);
+ setAccessControlHeaders(request, response);
+ response.setStatus(HttpResponseStatus.OK);
+ printParseOnlyValueResult(sessionOutput, parseOnlyResult);
ResultUtil.printStatus(sessionOutput, execution.getResultStatus());
+ } else {
+ Map<String, byte[]> statementParams = org.apache.asterix.app.translator.RequestParameters
+ .serializeParameterValues(param.getStatementParams());
+
+ setAccessControlHeaders(request, response);
+ 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);
@@ -553,6 +577,18 @@
}
}
+ protected ResultUtil.ParseOnlyResult parseStatement(String statementsText) throws CompilationException {
+ IParserFactory factory = compilationProvider.getParserFactory();
+ IParser parser = factory.createParser(statementsText);
+ List<Statement> stmts = parser.parse();
+ QueryTranslator.validateStatements(stmts);
+ Query query = (Query) stmts.get(stmts.size() - 1);
+ Set<VariableExpr> extVars =
+ compilationProvider.getRewriterFactory().createQueryRewriter().getExternalVariables(query.getBody());
+ ResultUtil.ParseOnlyResult parseOnlyResult = new ResultUtil.ParseOnlyResult(extVars);
+ return parseOnlyResult;
+ }
+
protected void executeStatement(String statementsText, SessionOutput sessionOutput,
ResultProperties resultProperties, Stats stats, QueryServiceRequestParameters param,
RequestExecutionState execution, Map<String, String> optionalParameters,
@@ -646,10 +682,19 @@
// do nothing
}
- private void printWarnings(PrintWriter pw, List<ExecutionWarning> warnings) {
+ protected void printWarnings(PrintWriter pw, List<ExecutionWarning> warnings) {
ResultUtil.printWarnings(pw, warnings);
}
+ protected void printParseOnlyValueResult(SessionOutput output, ResultUtil.ParseOnlyResult parseOnlyResult) {
+ final PrintWriter pw = output.out();
+ pw.print("\t\"");
+ pw.print(ResultFields.RESULTS.str()); //TODO: use ResultUtil, ResultPrinter
+ pw.print("\":");
+ pw.print(parseOnlyResult.asJson());
+ pw.print(",\n");
+ }
+
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/api/http/server/ResultUtil.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
index 8824f6a..fa3c03d 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ResultUtil.java
@@ -24,9 +24,11 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -35,6 +37,8 @@
import org.apache.asterix.app.result.ResultReader;
import org.apache.asterix.common.api.IApplicationContext;
import org.apache.asterix.lang.aql.parser.TokenMgrError;
+import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.translator.IStatementExecutor.Stats;
import org.apache.asterix.translator.SessionOutput;
@@ -379,4 +383,52 @@
return (app, status) -> app.append("\t\"").append(AbstractQueryApiServlet.ResultFields.STATUS.str())
.append("\": \"").append(status).append("\",\n");
}
+
+ public static class ParseOnlyResult {
+ private Set<VariableExpr> externalVariables;
+
+ private static final String STMT_PARAM_LBL = "statement-parameters";
+
+ public ParseOnlyResult(Set<VariableExpr> extVars) {
+ this.externalVariables = extVars;
+ }
+
+ public String asJson() {
+
+ ArrayList<String> positionalVars = new ArrayList<>();
+ ArrayList<String> namedVars = new ArrayList<>();
+
+ for (VariableExpr extVarRef : externalVariables) {
+ String varname = extVarRef.getVar().getValue();
+ if (SqlppVariableUtil.isPositionalVariableIdentifier(extVarRef.getVar())) {
+ positionalVars.add(SqlppVariableUtil.toUserDefinedName(varname));
+ } else {
+ namedVars.add(SqlppVariableUtil.toUserDefinedName(varname));
+ }
+ }
+ Collections.sort(positionalVars);
+ Collections.sort(namedVars);
+ final StringBuilder output = new StringBuilder();
+ output.append("{\"").append(STMT_PARAM_LBL).append("\":[");
+ boolean first = true;
+ for (String posVar : positionalVars) {
+ if (first) {
+ first = false;
+ } else {
+ output.append(",");
+ }
+ output.append(posVar);
+ }
+ for (String namedVar : namedVars) {
+ if (first) {
+ first = false;
+ } else {
+ output.append(",");
+ }
+ output.append("\"").append(namedVar).append("\"");
+ }
+ output.append("]}");
+ return output.toString();
+ }
+ }
}
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 9139d14..64a3d51 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
@@ -65,7 +65,6 @@
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.common.exceptions.MetadataException;
-import org.apache.asterix.common.exceptions.RuntimeDataException;
import org.apache.asterix.common.functions.FunctionSignature;
import org.apache.asterix.common.utils.JobUtils;
import org.apache.asterix.common.utils.JobUtils.ProgressState;
@@ -2932,13 +2931,13 @@
}
}
- protected void validateStatements(List<Statement> statements) throws RuntimeDataException {
- if (statements.stream().filter(this::isNotAllowedMultiStatement).count() > 1) {
- throw new RuntimeDataException(ErrorCode.UNSUPPORTED_MULTIPLE_STATEMENTS);
+ public static void validateStatements(List<Statement> statements) throws CompilationException {
+ if (statements.stream().filter(QueryTranslator::isNotAllowedMultiStatement).count() > 1) {
+ throw new CompilationException(ErrorCode.UNSUPPORTED_MULTIPLE_STATEMENTS);
}
}
- protected boolean isNotAllowedMultiStatement(Statement statement) {
+ protected static boolean isNotAllowedMultiStatement(Statement statement) {
switch (statement.getKind()) {
case DATAVERSE_DECL:
case FUNCTION_DECL:
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 bd7510f..3aa8807 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
@@ -132,7 +132,7 @@
private static final Pattern HANDLE_VARIABLE_PATTERN = Pattern.compile("handlevariable=(\\w+)");
private static final Pattern VARIABLE_REF_PATTERN = Pattern.compile("\\$(\\w+)");
private static final Pattern HTTP_PARAM_PATTERN =
- Pattern.compile("param (?<name>[\\w$]+)(?::(?<type>\\w+))?=(?<value>.*)", Pattern.MULTILINE);
+ Pattern.compile("param (?<name>[\\w-$]+)(?::(?<type>\\w+))?=(?<value>.*)", Pattern.MULTILINE);
private static final Pattern HTTP_BODY_PATTERN = Pattern.compile("body=(.*)", Pattern.MULTILINE);
private static final Pattern HTTP_STATUSCODE_PATTERN = Pattern.compile("statuscode (.*)", Pattern.MULTILINE);
private static final Pattern MAX_RESULT_READS_PATTERN =
@@ -890,6 +890,7 @@
break;
case "query":
case "async":
+ case "parse":
case "deferred":
case "metrics":
// isDmlRecoveryTest: insert Crash and Recovery
@@ -1220,8 +1221,14 @@
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;
+ default:
+ resultStream = ResultExtractor.extract(resultStream);
+ break;
+ }
} else {
String handleVar = getHandleVariable(statement);
resultStream = executeQueryService(statement, fmt, uri,
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..92edf47
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/ParseOnlyTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+@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/runtimets/queries/parseonly/001/parseonly_01.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.1.query.sqlpp
new file mode 100644
index 0000000..e24410f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.1.query.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-only:string=true
+
+select count(*) from ChirpUsers where name=$qname;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.2.query.sqlpp
new file mode 100644
index 0000000..ce0bf45
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.2.query.sqlpp
@@ -0,0 +1,54 @@
+/*
+ * 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-only:string=true
+
+// requesttype=application/json
+
+{
+ "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/queries/parseonly/001/parseonly_01.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.3.query.sqlpp
new file mode 100644
index 0000000..3730708
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.3.query.sqlpp
@@ -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.
+ */
+
+/*
+ * Description : Test named statement parameters with json encoded request
+ * Expected Res : Success
+ * Date : Jun 2018
+ */
+
+-- param parse-only:string=true
+
+
+select count(*) from ChirpUsers where field1=$qname and field2=$val and field3=$1;
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.4.query.sqlpp
new file mode 100644
index 0000000..2a25662
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.4.query.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * 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-only:string=true
+
+
+select $p_int, $p_str
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.5.query.sqlpp
new file mode 100644
index 0000000..9975f8b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/001/parseonly_01.5.query.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * 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-only:string=true
+
+// requesttype=application/json
+
+
+{
+ "t1": $p_int + ? + $1,
+ "t2": $p_str || ? || $2
+}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/002/parseonly_02.1.parse.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/002/parseonly_02.1.parse.sqlpp
new file mode 100644
index 0000000..13b9de5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/parseonly/002/parseonly_02.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 : Failure
+ * Date : Jun 2018
+ */
+
+-- param parse-only:string=true
+
+select count(*) from ChirpUsers name=$qname"
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.1.adm
new file mode 100644
index 0000000..70522ea
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.1.adm
@@ -0,0 +1 @@
+{"statement-parameters":["qname"]}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.2.adm
new file mode 100644
index 0000000..3f0a194
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.2.adm
@@ -0,0 +1 @@
+{"statement-parameters":["p_arr","p_bool","p_dbl","p_dec","p_int","p_null","p_obj","p_str"]}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.3.adm
new file mode 100644
index 0000000..d2b0d34
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.3.adm
@@ -0,0 +1 @@
+{"statement-parameters":[1,"qname","val"]}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.4.adm
new file mode 100644
index 0000000..782b0af
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.4.adm
@@ -0,0 +1 @@
+{"statement-parameters":["p_int","p_str"]}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.5.adm
new file mode 100644
index 0000000..c7715a1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/parseonly/001/parseonly_01.5.adm
@@ -0,0 +1 @@
+{"statement-parameters":[1,2,"p_int","p_str"]}
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..f7cea01
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_parseonly.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ! 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-case FilePath="parseonly">
+ <compilation-unit name="001">
+ <output-dir compare="Text">001</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="parseonly">
+ <compilation-unit name="002">
+ <output-dir compare="Text">named_02</output-dir>
+ <expected-error>ASX1001</expected-error>
+ </compilation-unit>
+ </test-case>
+ </test-group>
+</test-suite>
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
index 61cecfb..9aaf5b7 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
@@ -143,6 +143,11 @@
return gfc.getCalls();
}
+ @Override
+ public Set<VariableExpr> getExternalVariables(Expression expr) {
+ throw new UnsupportedOperationException("getExternalVariables not implemented for AQL");
+ }
+
private static class GatherFunctionCalls extends GatherFunctionCallsVisitor implements IAQLVisitor<Void, Void> {
public GatherFunctionCalls() {
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
index 8f3b8a9..7500ab9 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
@@ -24,6 +24,7 @@
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
import org.apache.asterix.lang.common.statement.FunctionDecl;
import org.apache.asterix.lang.common.struct.VarIdentifier;
@@ -53,4 +54,9 @@
*/
Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException;
+ /**
+ * Find all external variables (positional and named variables) in given expression
+ */
+ Set<VariableExpr> getExternalVariables(Expression expr) throws CompilationException;
+
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index 69a3e5e..942fc88 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -22,6 +22,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.HashSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -33,6 +34,7 @@
import org.apache.asterix.lang.common.base.IReturningStatement;
import org.apache.asterix.lang.common.clause.LetClause;
import org.apache.asterix.lang.common.expression.CallExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
import org.apache.asterix.lang.common.statement.FunctionDecl;
import org.apache.asterix.lang.common.struct.Identifier;
@@ -77,6 +79,9 @@
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil;
import org.apache.asterix.lang.sqlpp.util.SqlppAstPrintUtil;
+import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
+import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor;
+import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
import org.apache.asterix.metadata.declared.MetadataProvider;
import org.apache.hyracks.algebricks.common.utils.Pair;
@@ -297,6 +302,19 @@
return gfc.getCalls();
}
+ @Override
+ public Set<VariableExpr> getExternalVariables(Expression expr) throws CompilationException {
+ Set<VariableExpr> freeVars = SqlppVariableUtil.getFreeVariables(expr);
+
+ Set<VariableExpr> extVars = new HashSet<>();
+ for (VariableExpr ve : freeVars) {
+ if (SqlppVariableUtil.isExternalVariableReference(ve)) {
+ extVars.add(ve);
+ }
+ }
+ return extVars;
+ }
+
private static class GatherFunctionCalls extends GatherFunctionCallsVisitor implements ISqlppVisitor<Void, Void> {
public GatherFunctionCalls() {
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 8fabf13..f71ae5b 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
@@ -19,7 +19,6 @@
package org.apache.asterix.lang.sqlpp.util;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -39,7 +38,6 @@
import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause;
import org.apache.asterix.lang.sqlpp.clause.FromClause;
import org.apache.asterix.lang.sqlpp.clause.FromTerm;
-import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor;
import org.apache.hyracks.algebricks.common.utils.Pair;
@@ -80,6 +78,10 @@
if (varName.startsWith(USER_VAR_PREFIX)) {
return varName.substring(1);
}
+ if (varName.startsWith(EXTERNAL_VAR_PREFIX)) {
+ return varName.substring(1);
+ }
+
return varName;
}
@@ -95,6 +97,15 @@
return EXTERNAL_VAR_PREFIX + varName;
}
+ public static boolean isPositionalVariableIdentifier(VarIdentifier varId) {
+ try {
+ Integer.parseInt(toUserDefinedName(varId.getValue()));
+ return true;
+ } catch (NumberFormatException ignored) {
+ return false;
+ }
+ }
+
public static boolean isExternalVariableIdentifier(VarIdentifier varId) {
return varId.getValue().startsWith(EXTERNAL_VAR_PREFIX);
}