[NO ISSUE][API] Support client-type parameter in QueryServiceServlet
- user model changes: no
- storage format changes: no
- interface changes: yes
Details:
- QueryServiceServlet supports 'client-type' parameter that
indicates whether a client is a generic client or a JDBC driver
- Print field names and types in result signature if client
is the JDBC driver
- Extract common fields from ResultsPrinter and NcResultPrinter
into AbstractResultsPrinter
- Add testcases
Change-Id: Ia927091c9dec508b0c799ec8170765e43b847a39
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/13623
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
index 511b74cf..f33bb01 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
@@ -21,13 +21,17 @@
import java.io.Serializable;
public class ExecutionPlans implements Serializable {
- private static final long serialVersionUID = 6853904213354224457L;
+ private static final long serialVersionUID = 6853904213354224458L;
private String expressionTree;
private String rewrittenExpressionTree;
private String logicalPlan;
private String optimizedLogicalPlan;
private String job;
+ private String signature;
+ private String statementCategory;
+ private String statementParameters;
+ private boolean explainOnly;
public String getExpressionTree() {
return expressionTree;
@@ -68,4 +72,36 @@
public void setJob(String job) {
this.job = job;
}
+
+ public String getSignature() {
+ return signature;
+ }
+
+ public void setSignature(String signature) {
+ this.signature = signature;
+ }
+
+ public String getStatementCategory() {
+ return statementCategory;
+ }
+
+ public void setStatementCategory(String statementCategory) {
+ this.statementCategory = statementCategory;
+ }
+
+ public String getStatementParameters() {
+ return statementParameters;
+ }
+
+ public void setStatementParameters(String statementParameters) {
+ this.statementParameters = statementParameters;
+ }
+
+ public boolean isExplainOnly() {
+ return explainOnly;
+ }
+
+ public void setExplainOnly(boolean explainOnly) {
+ this.explainOnly = explainOnly;
+ }
}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlansJsonPrintUtil.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlansJsonPrintUtil.java
index c83e865..ad89ad6 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlansJsonPrintUtil.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlansJsonPrintUtil.java
@@ -18,6 +18,7 @@
*/
package org.apache.asterix.translator;
+import static org.apache.asterix.translator.SessionConfig.PlanFormat.JSON;
import static org.apache.asterix.translator.SessionConfig.PlanFormat.STRING;
import org.apache.hyracks.util.JSONUtil;
@@ -29,6 +30,9 @@
private static final String REWRITTEN_EXPRESSION_TREE_LBL = "rewrittenExpressionTree";
public static final String OPTIMIZED_LOGICAL_PLAN_LBL = "optimizedLogicalPlan";
private static final String JOB_LBL = "job";
+ private static final String STATEMENT_CATEGORY_LBL = "statementCategory";
+ private static final String STATEMENT_PARAMETERS_LBL = "statementParameters";
+ private static final String EXPLAIN_ONLY_LBL = "explainOnly";
private ExecutionPlansJsonPrintUtil() {
}
@@ -42,6 +46,11 @@
appendNonNull(output, LOGICAL_PLAN_LBL, plans.getLogicalPlan(), format);
appendNonNull(output, OPTIMIZED_LOGICAL_PLAN_LBL, plans.getOptimizedLogicalPlan(), format);
appendNonNull(output, JOB_LBL, plans.getJob(), format);
+ appendNonNull(output, STATEMENT_CATEGORY_LBL, plans.getStatementCategory(), STRING);
+ appendNonNull(output, STATEMENT_PARAMETERS_LBL, plans.getStatementParameters(), JSON);
+ if (plans.isExplainOnly()) {
+ appendNonNull(output, EXPLAIN_ONLY_LBL, Boolean.toString(plans.isExplainOnly()), JSON);
+ }
appendOutputPostfix(output);
return output.toString();
}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java
index 00342b6..ab1061f 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/IRequestParameters.java
@@ -47,7 +47,7 @@
/**
* @return a reference on which to write properties of executed queries (e.g. what kind of statement was parsed
- * by the parser)
+ * by the parser)
*/
StatementProperties getStatementProperties();
@@ -63,8 +63,8 @@
/**
* @return a bitmask that restricts which statement
- * {@link org.apache.asterix.lang.common.base.Statement.Category categories} are permitted for this request,
- * {@code 0} if all categories are allowed
+ * {@link org.apache.asterix.lang.common.base.Statement.Category categories} are permitted for this request,
+ * {@code 0} if all categories are allowed
*/
int getStatementCategoryRestrictionMask();
@@ -76,8 +76,10 @@
boolean isSkipAdmissionPolicy();
+ boolean isPrintSignature();
+
/**
* @return canonical name of the default dataverse for this statement
*/
String getDefaultDataverseName();
-}
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
index 33646f0..4e294ec 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java
@@ -21,6 +21,7 @@
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
@@ -39,7 +40,7 @@
* <li>It allows you to specify output format-specific parameters.
*/
public class SessionConfig implements Serializable {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
/**
* Used to specify the output format for the primary execution.
@@ -73,6 +74,11 @@
}
}
+ public enum ClientType {
+ ASTERIX,
+ JDBC
+ }
+
/**
* Produce out-of-band output for Hyracks Job.
*/
@@ -128,6 +134,9 @@
*/
public static final String FORMAT_QUOTE_RECORD = "quote-record";
+ // Client type
+ private ClientType clientType;
+
// Output format.
private OutputFormat fmt;
private PlanFormat planFormat;
@@ -175,6 +184,18 @@
this.generateJobSpec = generateJobSpec;
this.flags = new HashMap<>();
this.planFormat = planFormat;
+ this.clientType = ClientType.ASTERIX;
+ }
+
+ /**
+ * Retrieve the client type for this execution.
+ */
+ public ClientType getClientType() {
+ return this.clientType;
+ }
+
+ public void setClientType(ClientType clientType) {
+ this.clientType = Objects.requireNonNull(clientType);
}
/**
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index 9292296..53137e6 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -33,7 +33,9 @@
import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslator;
import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslatorFactory;
+import org.apache.asterix.api.http.server.ResultUtil;
import org.apache.asterix.app.result.fields.ExplainOnlyResultsPrinter;
+import org.apache.asterix.app.result.fields.SignaturePrinter;
import org.apache.asterix.common.api.INodeJobTracker;
import org.apache.asterix.common.api.IResponsePrinter;
import org.apache.asterix.common.config.CompilerProperties;
@@ -278,14 +280,34 @@
}
}
}
+
+ if (conf.getClientType() == SessionConfig.ClientType.JDBC) {
+ executionPlans.setStatementCategory(Statement.Category.toString(getStatementCategory(query, statement)));
+ if (!conf.isExecuteQuery()) {
+ String stmtParams = ResultUtil.ParseOnlyResult.printStatementParameters(externalVars.keySet(), v -> v);
+ executionPlans.setStatementParameters(stmtParams);
+ }
+ if (isExplainOnly) {
+ executionPlans.setExplainOnly(true);
+ } else if (isQuery) {
+ executionPlans.setSignature(SignaturePrinter.generateFlatSignature(resultMetadata));
+ }
+ }
+
+ boolean printSignature = isQuery && requestParameters != null && requestParameters.isPrintSignature();
+
if (isExplainOnly) {
- printPlanAsResult(metadataProvider, output, printer);
+ printPlanAsResult(metadataProvider, output, printer, printSignature);
if (!conf.is(SessionConfig.OOB_OPTIMIZED_LOGICAL_PLAN)) {
executionPlans.setOptimizedLogicalPlan(null);
}
return null;
}
+ if (printSignature) {
+ printer.addResultPrinter(SignaturePrinter.newInstance(executionPlans));
+ }
+
if (!conf.isGenerateJobSpec()) {
return null;
}
@@ -328,9 +350,12 @@
return spec;
}
- private void printPlanAsResult(MetadataProvider metadataProvider, SessionOutput output, IResponsePrinter printer)
- throws AlgebricksException {
+ private void printPlanAsResult(MetadataProvider metadataProvider, SessionOutput output, IResponsePrinter printer,
+ boolean printSignature) throws AlgebricksException {
try {
+ if (printSignature) {
+ printer.addResultPrinter(SignaturePrinter.INSTANCE);
+ }
printer.addResultPrinter(new ExplainOnlyResultsPrinter(metadataProvider.getApplicationContext(),
executionPlans.getOptimizedLogicalPlan(), output));
printer.printResults();
@@ -362,6 +387,11 @@
: PlanPrettyPrinter.createStringPlanPrettyPrinter();
}
+ private byte getStatementCategory(Query query, ICompiledDmlStatement statement) {
+ return statement != null ? statement.getCategory()
+ : query != null ? Statement.Category.QUERY : Statement.Category.DDL;
+ }
+
public void executeJobArray(IHyracksClientConnection hcc, JobSpecification[] specs, PrintWriter out)
throws Exception {
for (JobSpecification spec : specs) {
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
index 68d7b08..e6ba15f 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java
@@ -32,6 +32,7 @@
import org.apache.asterix.app.message.ExecuteStatementResponseMessage;
import org.apache.asterix.app.result.ResponsePrinter;
import org.apache.asterix.app.result.fields.NcResultPrinter;
+import org.apache.asterix.app.result.fields.SignaturePrinter;
import org.apache.asterix.common.api.IApplicationContext;
import org.apache.asterix.common.api.IRequestReference;
import org.apache.asterix.common.config.GlobalConfig;
@@ -122,6 +123,9 @@
// if the was no error, we can set the result status to success
executionState.setStatus(ResultStatus.SUCCESS, HttpResponseStatus.OK);
updateStatsFromCC(stats, responseMsg);
+ if (param.isSignature() && delivery != IStatementExecutor.ResultDelivery.ASYNC && !param.isParseOnly()) {
+ responsePrinter.addResultPrinter(SignaturePrinter.newInstance(responseMsg.getExecutionPlans()));
+ }
if (hasResult(responseMsg)) {
responsePrinter.addResultPrinter(
new NcResultPrinter(appCtx, responseMsg, getResultSet(), delivery, sessionOutput, stats));
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 412e5c5..084bca7 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
@@ -33,6 +33,7 @@
import org.apache.asterix.common.exceptions.RuntimeDataException;
import org.apache.asterix.translator.IStatementExecutor.ResultDelivery;
import org.apache.asterix.translator.IStatementExecutor.Stats.ProfileType;
+import org.apache.asterix.translator.SessionConfig.ClientType;
import org.apache.asterix.translator.SessionConfig.OutputFormat;
import org.apache.asterix.translator.SessionConfig.PlanFormat;
import org.apache.commons.lang3.StringUtils;
@@ -60,6 +61,7 @@
STATEMENT("statement"),
FORMAT("format"),
CLIENT_ID("client_context_id"),
+ CLIENT_TYPE("client-type"),
DATAVERSE("dataverse"),
PRETTY("pretty"),
MODE("mode"),
@@ -108,6 +110,8 @@
private static final Map<String, PlanFormat> planFormats = ImmutableMap.of(HttpUtil.ContentType.JSON,
PlanFormat.JSON, "clean_json", PlanFormat.JSON, "string", PlanFormat.STRING);
+ private static final Map<String, ClientType> clientTypes =
+ ImmutableMap.of("asterix", ClientType.ASTERIX, "jdbc", ClientType.JDBC);
private static final Map<String, Boolean> booleanValues =
ImmutableMap.of(Boolean.TRUE.toString(), Boolean.TRUE, Boolean.FALSE.toString(), Boolean.FALSE);
private static final Map<String, Boolean> csvHeaderValues =
@@ -119,6 +123,7 @@
private String statement;
private String clientContextID;
private String dataverse;
+ private ClientType clientType = ClientType.ASTERIX;
private OutputFormat format = OutputFormat.CLEAN_JSON;
private ResultDelivery mode = ResultDelivery.IMMEDIATE;
private PlanFormat planFormat = PlanFormat.JSON;
@@ -202,6 +207,14 @@
this.clientContextID = clientContextID;
}
+ public ClientType getClientType() {
+ return clientType;
+ }
+
+ public void setClientType(ClientType clientType) {
+ this.clientType = Objects.requireNonNull(clientType);
+ }
+
public String getDataverse() {
return dataverse;
}
@@ -361,6 +374,7 @@
object.put("pretty", pretty);
object.put("mode", mode.getName());
object.put("clientContextID", clientContextID);
+ object.put("clientType", clientType.toString());
object.put("dataverse", dataverse);
object.put("format", format.toString());
object.put("timeout", timeout);
@@ -459,6 +473,7 @@
setMultiStatement(parseBoolean(req, Parameter.MULTI_STATEMENT.str(), valGetter, isMultiStatement()));
setJob(parseBoolean(req, Parameter.JOB.str(), valGetter, isJob()));
setSignature(parseBoolean(req, Parameter.SIGNATURE.str(), valGetter, isSignature()));
+ setClientType(parseIfExists(req, Parameter.CLIENT_TYPE.str(), valGetter, getClientType(), clientTypes::get));
}
protected void setExtraParams(JsonNode jsonRequest) throws HyracksDataException {
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 26e4b96..b46c299 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
@@ -46,7 +46,6 @@
import org.apache.asterix.app.result.fields.PlansPrinter;
import org.apache.asterix.app.result.fields.ProfilePrinter;
import org.apache.asterix.app.result.fields.RequestIdPrinter;
-import org.apache.asterix.app.result.fields.SignaturePrinter;
import org.apache.asterix.app.result.fields.StatusPrinter;
import org.apache.asterix.app.result.fields.TypePrinter;
import org.apache.asterix.app.result.fields.WarningsPrinter;
@@ -297,7 +296,7 @@
responsePrinter.addResultPrinter(new ParseOnlyResultPrinter(parseOnlyResult));
} else {
Map<String, byte[]> statementParams = org.apache.asterix.app.translator.RequestParameters
- .serializeParameterValues(param.getStatementParams());
+ .serializeParameterValues(param.getStatementParams(), sessionOutput.config().fmt());
setAccessControlHeaders(request, response);
stats.setProfileType(param.getProfileType());
IStatementExecutor.StatementProperties statementProperties =
@@ -335,9 +334,6 @@
if (param.getClientContextID() != null && !param.getClientContextID().isEmpty()) {
responsePrinter.addHeaderPrinter(new ClientContextIdPrinter(param.getClientContextID()));
}
- if (param.isSignature() && delivery != ResultDelivery.ASYNC && !param.isParseOnly()) {
- responsePrinter.addHeaderPrinter(SignaturePrinter.INSTANCE);
- }
if (sessionOutput.config().fmt() == SessionConfig.OutputFormat.ADM
|| sessionOutput.config().fmt() == SessionConfig.OutputFormat.CSV) {
responsePrinter.addHeaderPrinter(new TypePrinter(sessionOutput.config()));
@@ -484,8 +480,10 @@
String handleUrl = getHandleUrl(param.getHost(), param.getPath(), delivery);
sessionOutput.setHandleAppender(ResultUtil.createResultHandleAppender(handleUrl));
SessionConfig sessionConfig = sessionOutput.config();
+ SessionConfig.ClientType clientType = param.getClientType();
SessionConfig.OutputFormat format = param.getFormat();
SessionConfig.PlanFormat planFormat = param.getPlanFormat();
+ sessionConfig.setClientType(clientType);
sessionConfig.setFmt(format);
sessionConfig.setPlanFormat(planFormat);
sessionConfig.setMaxWarnings(param.getMaxWarnings());
@@ -517,9 +515,11 @@
IRequestReference requestReference, String statementsText, IResultSet resultSet,
ResultProperties resultProperties, Stats stats, IStatementExecutor.StatementProperties statementProperties,
Map<String, String> optionalParameters, Map<String, IAObject> stmtParams, int stmtCategoryRestriction) {
- return new RequestParameters(requestReference, statementsText, resultSet, resultProperties, stats,
- statementProperties, null, param.getClientContextID(), param.getDataverse(), optionalParameters,
- stmtParams, param.isMultiStatement(), stmtCategoryRestriction);
+ RequestParameters requestParameters = new RequestParameters(requestReference, statementsText, resultSet,
+ resultProperties, stats, statementProperties, null, param.getClientContextID(), param.getDataverse(),
+ optionalParameters, stmtParams, param.isMultiStatement(), stmtCategoryRestriction);
+ requestParameters.setPrintSignature(param.isSignature());
+ return requestParameters;
}
protected static boolean isPrintingProfile(IStatementExecutor.Stats stats) {
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 045d64c..cf535d9 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
@@ -26,9 +26,11 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -39,6 +41,7 @@
import org.apache.asterix.app.result.fields.StatusPrinter;
import org.apache.asterix.common.api.IApplicationContext;
import org.apache.asterix.lang.common.expression.VariableExpr;
+import org.apache.asterix.lang.common.struct.VarIdentifier;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.translator.IStatementExecutor.Stats;
@@ -318,7 +321,8 @@
}
public static class ParseOnlyResult {
- private Set<VariableExpr> externalVariables;
+
+ private final Set<VariableExpr> externalVariables;
private static final String STMT_PARAM_LBL = "statement-parameters";
@@ -327,13 +331,29 @@
}
public String asJson() {
+ StringBuilder output = new StringBuilder();
+ output.append("{\"").append(STMT_PARAM_LBL).append("\":");
+ printStatementParameters(externalVariables, VariableExpr::getVar, output);
+ output.append('}');
+ return output.toString();
+ }
- ArrayList<String> positionalVars = new ArrayList<>();
- ArrayList<String> namedVars = new ArrayList<>();
+ public static <T> String printStatementParameters(Collection<T> externalVariables,
+ Function<T, VarIdentifier> varIdentAccessor) {
+ StringBuilder output = new StringBuilder(externalVariables.size() * 12);
+ printStatementParameters(externalVariables, varIdentAccessor, output);
+ return output.toString();
+ }
- for (VariableExpr extVarRef : externalVariables) {
- String varname = extVarRef.getVar().getValue();
- if (SqlppVariableUtil.isPositionalVariableIdentifier(extVarRef.getVar())) {
+ private static <T> void printStatementParameters(Collection<T> externalVariables,
+ Function<T, VarIdentifier> varIdentAccessor, StringBuilder output) {
+ List<String> positionalVars = new ArrayList<>();
+ List<String> namedVars = new ArrayList<>();
+
+ for (T extVarItem : externalVariables) {
+ VarIdentifier extVar = varIdentAccessor.apply(extVarItem);
+ String varname = extVar.getValue();
+ if (SqlppVariableUtil.isPositionalVariableIdentifier(extVar)) {
positionalVars.add(SqlppVariableUtil.toUserDefinedName(varname));
} else {
namedVars.add(SqlppVariableUtil.toUserDefinedName(varname));
@@ -341,14 +361,14 @@
}
Collections.sort(positionalVars);
Collections.sort(namedVars);
- final StringBuilder output = new StringBuilder();
- output.append("{\"").append(STMT_PARAM_LBL).append("\":[");
+
+ output.append('[');
boolean first = true;
for (String posVar : positionalVars) {
if (first) {
first = false;
} else {
- output.append(",");
+ output.append(',');
}
output.append(posVar);
}
@@ -358,10 +378,9 @@
} else {
output.append(",");
}
- output.append("\"").append(namedVar).append("\"");
+ output.append('"').append(namedVar).append('"');
}
- output.append("]}");
- return output.toString();
+ output.append(']');
}
}
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/AbstractResultsPrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/AbstractResultsPrinter.java
new file mode 100644
index 0000000..621fde1
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/AbstractResultsPrinter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.app.result.fields;
+
+import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.SessionOutput;
+
+abstract class AbstractResultsPrinter implements IResponseFieldPrinter {
+ public static final String FIELD_NAME = "results";
+
+ protected final IApplicationContext appCtx;
+ protected final IStatementExecutor.Stats stats;
+ protected final SessionOutput sessionOutput;
+
+ AbstractResultsPrinter(IApplicationContext appCtx, IStatementExecutor.Stats stats, SessionOutput sessionOutput) {
+ this.appCtx = appCtx;
+ this.stats = stats;
+ this.sessionOutput = sessionOutput;
+ }
+
+ @Override
+ public String getName() {
+ return FIELD_NAME;
+ }
+}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java
index ccf970c..d98472e 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcResultPrinter.java
@@ -26,7 +26,6 @@
import org.apache.asterix.app.result.ResponsePrinter;
import org.apache.asterix.app.result.ResultReader;
import org.apache.asterix.common.api.IApplicationContext;
-import org.apache.asterix.common.api.IResponseFieldPrinter;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.translator.IStatementExecutor;
import org.apache.asterix.translator.SessionOutput;
@@ -36,24 +35,19 @@
import org.apache.hyracks.api.result.IResultSet;
import org.apache.hyracks.api.result.ResultSetId;
-public class NcResultPrinter implements IResponseFieldPrinter {
+public class NcResultPrinter extends AbstractResultsPrinter {
private final IStatementExecutor.ResultDelivery delivery;
private final ExecuteStatementResponseMessage responseMsg;
- private final IApplicationContext appCtx;
private final IResultSet resultSet;
- private final SessionOutput sessionOutput;
- private final IStatementExecutor.Stats stats;
public NcResultPrinter(IApplicationContext appCtx, ExecuteStatementResponseMessage responseMsg,
IResultSet resultSet, IStatementExecutor.ResultDelivery delivery, SessionOutput sessionOutput,
IStatementExecutor.Stats stats) {
- this.appCtx = appCtx;
+ super(appCtx, stats, sessionOutput);
this.responseMsg = responseMsg;
this.delivery = delivery;
this.resultSet = resultSet;
- this.sessionOutput = sessionOutput;
- this.stats = stats;
}
@Override
@@ -73,9 +67,4 @@
pw.append(responseMsg.getResult());
}
}
-
- @Override
- public String getName() {
- return ResultsPrinter.FIELD_NAME;
- }
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java
index 52198de..ee2d7d5 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/ResultsPrinter.java
@@ -23,37 +23,25 @@
import org.apache.asterix.api.http.server.ResultUtil;
import org.apache.asterix.app.result.ResultReader;
import org.apache.asterix.common.api.IApplicationContext;
-import org.apache.asterix.common.api.IResponseFieldPrinter;
import org.apache.asterix.om.types.ARecordType;
import org.apache.asterix.translator.IStatementExecutor;
import org.apache.asterix.translator.SessionOutput;
import org.apache.hyracks.api.exceptions.HyracksDataException;
-public class ResultsPrinter implements IResponseFieldPrinter {
+public class ResultsPrinter extends AbstractResultsPrinter {
- public static final String FIELD_NAME = "results";
- private final IApplicationContext appCtx;
private final ARecordType recordType;
private final ResultReader resultReader;
- private final IStatementExecutor.Stats stats;
- private final SessionOutput sessionOutput;
public ResultsPrinter(IApplicationContext appCtx, ResultReader resultReader, ARecordType recordType,
IStatementExecutor.Stats stats, SessionOutput sessionOutput) {
- this.appCtx = appCtx;
+ super(appCtx, stats, sessionOutput);
this.recordType = recordType;
this.resultReader = resultReader;
- this.stats = stats;
- this.sessionOutput = sessionOutput;
}
@Override
public void print(PrintWriter pw) throws HyracksDataException {
ResultUtil.printResults(appCtx, resultReader, sessionOutput, stats, recordType);
}
-
- @Override
- public String getName() {
- return FIELD_NAME;
- }
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java
index fe9d2be..048584c 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/SignaturePrinter.java
@@ -19,27 +19,147 @@
package org.apache.asterix.app.result.fields;
import java.io.PrintWriter;
+import java.util.LinkedHashSet;
+import java.util.List;
+import org.apache.asterix.api.common.ResultMetadata;
import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.annotations.IRecordTypeAnnotation;
+import org.apache.asterix.common.annotations.RecordFieldOrderAnnotation;
import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.om.typecomputer.impl.TypeComputeUtils;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.translator.ExecutionPlans;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.util.JSONUtil;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
public class SignaturePrinter implements IResponseFieldPrinter {
- public static final SignaturePrinter INSTANCE = new SignaturePrinter();
+ public static final SignaturePrinter INSTANCE = new SignaturePrinter(null);
private static final String FIELD_NAME = "signature";
+ private static final String NAME_FIELD_NAME = "name";
+ private static final String TYPE_FIELD_NAME = "type";
+ private static final String OPTIONAL_MODIFIER = "?";
+
+ private final String signature;
+
+ public SignaturePrinter(String signature) {
+ this.signature = signature;
+ }
@Override
public void print(PrintWriter pw) {
pw.print("\t\"");
pw.print(FIELD_NAME);
- pw.print("\": {\n");
- pw.print("\t");
- ResultUtil.printField(pw, "*", "*", false);
- pw.print("\n\t}");
+ pw.print("\": ");
+ if (signature == null) {
+ pw.print("{\n\t");
+ ResultUtil.printField(pw, "*", "*", false);
+ pw.print("\n\t}");
+ } else {
+ pw.print(signature);
+ }
}
@Override
public String getName() {
return FIELD_NAME;
}
+
+ public static String generateFlatSignature(ResultMetadata resultMetadata) {
+ List<Object> typeInfo = resultMetadata.getOutputTypes();
+ if (typeInfo == null || typeInfo.size() != 1) {
+ return null;
+ }
+ IAType outputType = TypeComputeUtils.getActualType((IAType) typeInfo.get(0));
+ if (outputType.getTypeTag() != ATypeTag.OBJECT) {
+ return null;
+ }
+ ARecordType outputRecordType = (ARecordType) outputType;
+ String[] fieldNames;
+ IAType[] fieldTypes;
+ Pair<String[], IAType[]> p = generateFlatSignatureFromOpenType(outputRecordType);
+ if (p != null) {
+ fieldNames = p.first;
+ fieldTypes = p.second;
+ } else {
+ fieldNames = outputRecordType.getFieldNames();
+ fieldTypes = outputRecordType.getFieldTypes();
+ }
+ if (fieldNames == null || fieldNames.length == 0) {
+ return null;
+ }
+ ObjectNode signatureNode = JSONUtil.createObject();
+ ArrayNode fieldNameArrayNode = signatureNode.putArray(NAME_FIELD_NAME);
+ ArrayNode fieldTypeArrayNode = signatureNode.putArray(TYPE_FIELD_NAME);
+ for (int i = 0, n = fieldNames.length; i < n; i++) {
+ fieldNameArrayNode.add(fieldNames[i]);
+ fieldTypeArrayNode.add(printFieldType(fieldTypes[i]));
+ }
+ return signatureNode.toString();
+ }
+
+ private static Pair<String[], IAType[]> generateFlatSignatureFromOpenType(ARecordType outputRecordType) {
+ if (!outputRecordType.isOpen() || !outputRecordType.knowsAllPossibleAdditonalFieldNames()) {
+ return null;
+ }
+ IRecordTypeAnnotation fieldOrderAnn =
+ outputRecordType.findAnnotation(IRecordTypeAnnotation.Kind.RECORD_FIELD_ORDER);
+ if (fieldOrderAnn == null) {
+ return null;
+ }
+ LinkedHashSet<String> fieldNamesOrdered = ((RecordFieldOrderAnnotation) fieldOrderAnn).getFieldNames();
+ int numFields = fieldNamesOrdered.size();
+
+ String[] fieldNames = new String[numFields];
+ IAType[] fieldTypes = new IAType[numFields];
+ int i = 0;
+ for (String fieldName : fieldNamesOrdered) {
+ IAType fieldType;
+ if (outputRecordType.isClosedField(fieldName)) {
+ fieldType = outputRecordType.getFieldType(fieldName);
+ } else if (outputRecordType.getAllPossibleAdditonalFieldNames().contains(fieldName)) {
+ fieldType = BuiltinType.ANY;
+ } else {
+ return null;
+ }
+ fieldNames[i] = fieldName;
+ fieldTypes[i] = fieldType;
+ i++;
+ }
+ return new Pair<>(fieldNames, fieldTypes);
+ }
+
+ private static String printFieldType(IAType type) {
+ boolean optional = false;
+ if (type.getTypeTag() == ATypeTag.UNION) {
+ AUnionType fieldTypeUnion = (AUnionType) type;
+ optional = fieldTypeUnion.isNullableType() || fieldTypeUnion.isMissableType();
+ type = fieldTypeUnion.getActualType();
+ }
+ String typeName;
+ switch (type.getTypeTag()) {
+ case OBJECT:
+ case ARRAY:
+ case MULTISET:
+ typeName = type.getDisplayName();
+ break;
+ default:
+ typeName = type.getTypeName();
+ break;
+ }
+ return optional ? typeName + OPTIONAL_MODIFIER : typeName;
+ }
+
+ public static SignaturePrinter newInstance(ExecutionPlans executionPlans) {
+ String signature = executionPlans.getSignature();
+ return signature != null ? new SignaturePrinter(signature) : INSTANCE;
+ }
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java
index e7af36b..7b634bd 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/RequestParameters.java
@@ -25,6 +25,7 @@
import org.apache.asterix.common.api.IRequestReference;
import org.apache.asterix.external.parser.JSONDataParser;
+import org.apache.asterix.external.parser.LosslessADMJSONDataParser;
import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider;
import org.apache.asterix.lang.common.base.Statement;
import org.apache.asterix.om.base.IAObject;
@@ -34,6 +35,7 @@
import org.apache.asterix.translator.IStatementExecutor.StatementProperties;
import org.apache.asterix.translator.IStatementExecutor.Stats;
import org.apache.asterix.translator.ResultProperties;
+import org.apache.asterix.translator.SessionConfig;
import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.api.result.IResultSet;
@@ -61,6 +63,7 @@
private final String defaultDataverseName;
private final boolean forceDropDataset;
private final boolean skipAdmissionPolicy;
+ private boolean printSignature;
public RequestParameters(IRequestReference requestReference, String statement, IResultSet resultSet,
ResultProperties resultProperties, Stats stats, StatementProperties statementProperties,
@@ -188,12 +191,21 @@
return defaultDataverseName;
}
- public static Map<String, byte[]> serializeParameterValues(Map<String, JsonNode> inParams)
- throws HyracksDataException {
+ public boolean isPrintSignature() {
+ return printSignature;
+ }
+
+ public void setPrintSignature(boolean printSignature) {
+ this.printSignature = printSignature;
+ }
+
+ public static Map<String, byte[]> serializeParameterValues(Map<String, JsonNode> inParams,
+ SessionConfig.OutputFormat format) throws HyracksDataException {
if (inParams == null || inParams.isEmpty()) {
return null;
}
- JSONDataParser parser = new JSONDataParser(null, null);
+ JSONDataParser parser = format == SessionConfig.OutputFormat.LOSSLESS_ADM_JSON
+ ? new LosslessADMJSONDataParser(null) : new JSONDataParser(null, null);
ByteArrayAccessibleOutputStream buffer = new ByteArrayAccessibleOutputStream();
DataOutputStream bufferDataOutput = new DataOutputStream(buffer);
Map<String, byte[]> m = new HashMap<>();
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 5ab5b01..f83ddb2 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
@@ -173,6 +173,10 @@
return extract(resultStream, EnumSet.of(ResultField.PLANS), resultCharset, OutputFormat.ADM, plans).getResult();
}
+ public static InputStream extractSignature(InputStream resultStream, Charset resultCharset) throws Exception {
+ return extract(resultStream, EnumSet.of(ResultField.SIGNATURE), resultCharset).getResult();
+ }
+
public static InputStream extractStatus(InputStream resultStream, Charset resultCharset) throws Exception {
return extract(resultStream, EnumSet.of(ResultField.STATUS), resultCharset).getResult();
}
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 bd618d5..95b77cc 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
@@ -238,6 +238,7 @@
private static final String METRICS_QUERY_TYPE = "metrics";
private static final String PROFILE_QUERY_TYPE = "profile";
private static final String PLANS_QUERY_TYPE = "plans";
+ private static final String SIGNATURE_QUERY_TYPE = "signature";
private static final HashMap<Integer, ITestServer> runningTestServers = new HashMap<>();
private static Map<String, InetSocketAddress> ncEndPoints;
@@ -1202,6 +1203,7 @@
case "metrics":
case "profile":
case "plans":
+ case "signature":
// isDmlRecoveryTest: insert Crash and Recovery
if (isDmlRecoveryTest) {
executeScript(pb, pb.environment().get("SCRIPT_HOME") + File.separator + "dml_recovery"
@@ -1577,6 +1579,9 @@
String[] plans = plans(statement);
resultStream = ResultExtractor.extractPlans(resultStream, responseCharset, plans);
break;
+ case SIGNATURE_QUERY_TYPE:
+ resultStream = ResultExtractor.extractSignature(resultStream, responseCharset);
+ break;
default:
extractedResult = ResultExtractor.extract(resultStream, responseCharset, fmt);
resultStream = extractedResult.getResult();
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java
index 24ca072..fcab213 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestHelper.java
@@ -41,6 +41,7 @@
import org.apache.asterix.om.base.IAObject;
import org.apache.asterix.testframework.xml.ParameterTypeEnum;
import org.apache.asterix.testframework.xml.TestCase;
+import org.apache.asterix.translator.SessionConfig;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FileUtils;
import org.apache.hyracks.util.file.FileUtil;
@@ -164,7 +165,8 @@
}
}
- return RequestParameters.deserializeParameterValues(RequestParameters.serializeParameterValues(stmtParams));
+ return RequestParameters.deserializeParameterValues(
+ RequestParameters.serializeParameterValues(stmtParams, SessionConfig.OutputFormat.CLEAN_JSON));
}
public static boolean equalJson(JsonNode expectedJson, JsonNode actualJson, boolean compareUnorderedArray,
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.2.plans.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.2.plans.sqlpp
new file mode 100644
index 0000000..2a4952f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.2.plans.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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 additional information returned when client-type=jdbc
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+select v from range(1,2) v where v between ? and ? ;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.3.plans.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.3.plans.sqlpp
new file mode 100644
index 0000000..d7217a4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.3.plans.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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 additional information returned when client-type=jdbc (with explain)
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+explain select v from range(1,2) v where v between ? and ? ;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.4.plans.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.4.plans.sqlpp
new file mode 100644
index 0000000..8d2bd74
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/compileonly/compileonly.4.plans.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * 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 additional information returned when client-type=jdbc (update statement)
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+drop dataverse test1 if exists;
+
+create dataverse test1;
+
+create dataset test1.t1(c1 int not unknown, c2 int) primary key c1;
+
+insert into test1.t1 [{"c1": 1, "c2": ? }, {"c1": 3, "c2": ? }];
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.1.signature.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.1.signature.sqlpp
new file mode 100644
index 0000000..3620bde
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.1.signature.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+-- param compile-only:string=true
+
+select v v1, v v2 from range(1,2) v where v > ?;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.2.signature.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.2.signature.sqlpp
new file mode 100644
index 0000000..37eee90
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.2.signature.sqlpp
@@ -0,0 +1,44 @@
+/*
+ * 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 returned signature when client-type=jdbc (primitive types)
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+select
+ int8(v) c_int8,
+ int16(v) c_int16,
+ int32(v) c_int32,
+ int64(v) c_int64,
+ float(v) c_float,
+ double(v) c_double,
+ string(v) c_string,
+ boolean(v) c_boolean,
+ datetime_from_unix_time_in_ms(v) c_datetime,
+ date_from_unix_time_in_days(v) c_date,
+ time_from_unix_time_in_ms(v) c_time,
+ duration_from_months(v) c_duration,
+ year_month_duration(duration_from_months(v)) c_year_month_duration,
+ day_time_duration(duration_from_ms(v)) c_day_time_month_duration,
+ null c_null
+from range(1,2) v
+where v > ?;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.3.signature.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.3.signature.sqlpp
new file mode 100644
index 0000000..07cc798
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.3.signature.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * 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 returned signature when client-type=jdbc (complex types)
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+select
+ v c_number,
+ { "f": v } c_record,
+ [ v, v ] c_array,
+ {{ v, v }} c_multiset
+from range(1,2) v
+where v > ?;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.4.signature.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.4.signature.sqlpp
new file mode 100644
index 0000000..3e3d170
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.4.signature.sqlpp
@@ -0,0 +1,32 @@
+/*
+ * 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 returned signature when client-type=jdbc (unknown types)
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+select
+ v c_number,
+ case when v = 1 then { "f": v } else [ v, v ] end c_case,
+ missing c_missing
+from range(1,2) v
+where v > ?;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.5.signature.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.5.signature.sqlpp
new file mode 100644
index 0000000..c776f31
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/api/signature/signature.5.signature.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.
+ */
+
+/*
+ * Test returned signature when client-type=jdbc (types from a dataset)
+ */
+
+-- param client-type:string=jdbc
+-- param compile-only:string=true
+
+select DataverseName, DatasetName, ViewDetails
+from Metadata.`Dataset`;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.2.adm
new file mode 100644
index 0000000..1ced420
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.2.adm
@@ -0,0 +1 @@
+{"statementCategory":"query","statementParameters":[1,2]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.3.adm
new file mode 100644
index 0000000..83b47f4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.3.adm
@@ -0,0 +1 @@
+{"statementCategory":"query","statementParameters":[1,2],"explainOnly":true}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.4.adm
new file mode 100644
index 0000000..63c482a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/compileonly/compileonly.4.adm
@@ -0,0 +1 @@
+{"statementCategory":"update","statementParameters":[1,2]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.1.adm
new file mode 100644
index 0000000..a10e23e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.1.adm
@@ -0,0 +1 @@
+{"*":"*"}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.2.adm
new file mode 100644
index 0000000..fae3584
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.2.adm
@@ -0,0 +1 @@
+{"name":["c_int8","c_int16","c_int32","c_int64","c_float","c_double","c_string","c_boolean","c_datetime","c_date","c_time","c_duration","c_year_month_duration","c_day_time_month_duration","c_null"],"type":["int8","int16","int32","int64","float","double","string","boolean","datetime","date","time","duration","year-month-duration","day-time-duration","null"]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.3.adm
new file mode 100644
index 0000000..86234be
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.3.adm
@@ -0,0 +1 @@
+{"name":["c_number","c_record","c_array","c_multiset"],"type":["int64","object","array","multiset"]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.4.adm
new file mode 100644
index 0000000..5c5ffd0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.4.adm
@@ -0,0 +1 @@
+{"name":["c_number","c_case","c_missing"],"type":["int64","any","any"]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.5.adm
new file mode 100644
index 0000000..48710ba
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/api/signature/signature.5.adm
@@ -0,0 +1 @@
+{"name":["DataverseName","DatasetName","ViewDetails"],"type":["string","string","any"]}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 65a676c..6450a1c 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -145,6 +145,11 @@
</compilation-unit>
</test-case>
<test-case FilePath="api">
+ <compilation-unit name="signature">
+ <output-dir compare="Text">signature</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="api">
<compilation-unit name="ignore-body-for-get">
<output-dir compare="Text">ignore-body-for-get</output-dir>
</compilation-unit>