Implement EXPLAIN for SQL++
- move some code from static methods in ResultUtils to a stateful
ResultPrinter to facilitate reuse (we create one ResultWriter per request)
- tiny cleanup in LogicalOperatorPrettyPrintVisitor
Change-Id: I7b7028fb243d494150cac525c73b2d77b0068646
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1020
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Yingyi Bu <buyingyi@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/javacc/AQLPlus.jj b/asterixdb/asterix-algebra/src/main/javacc/AQLPlus.jj
index 0e1e7a1..98cae63 100644
--- a/asterixdb/asterix-algebra/src/main/javacc/AQLPlus.jj
+++ b/asterixdb/asterix-algebra/src/main/javacc/AQLPlus.jj
@@ -487,7 +487,7 @@
Query Query()throws ParseException:
{
- Query query = new Query();
+ Query query = new Query(false);
Expression expr;
}
{
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 af17c05..d6864c1 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
@@ -18,6 +18,7 @@
*/
package org.apache.asterix.api.common;
+import java.io.IOException;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.ArrayList;
@@ -48,6 +49,7 @@
import org.apache.asterix.metadata.declared.AqlMetadataProvider;
import org.apache.asterix.om.util.AsterixAppContextInfo;
import org.apache.asterix.optimizer.base.RuleCollections;
+import org.apache.asterix.result.ResultUtils;
import org.apache.asterix.runtime.job.listener.JobEventListenerFactory;
import org.apache.asterix.transaction.management.service.transaction.JobIdFactory;
import org.apache.asterix.translator.CompiledStatements.ICompiledDmlStatement;
@@ -279,6 +281,16 @@
}
}
}
+ if (rwQ != null && rwQ.isExplain()) {
+ try {
+ LogicalOperatorPrettyPrintVisitor pvisitor = new LogicalOperatorPrettyPrintVisitor();
+ PlanPrettyPrinter.printPlan(plan, pvisitor, 0);
+ ResultUtils.displayResults(pvisitor.get().toString(), conf, new ResultUtils.Stats(), null);
+ return null;
+ } catch (IOException e) {
+ throw new AlgebricksException(e);
+ }
+ }
if (!conf.isGenerateJobSpec()) {
return null;
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java
index 05d9b3dd..d6065fb 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/aql/translator/QueryTranslator.java
@@ -2529,14 +2529,16 @@
boolean bActiveTxn = true;
metadataProvider.setMetadataTxnContext(mdTxnCtx);
MetadataLockManager.INSTANCE.queryBegin(activeDefaultDataverse, query.getDataverses(), query.getDatasets());
- JobSpecification compiled = null;
try {
- compiled = rewriteCompileQuery(metadataProvider, query, null);
+ JobSpecification compiled = rewriteCompileQuery(metadataProvider, query, null);
MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
bActiveTxn = false;
- if (sessionConfig.isExecuteQuery() && compiled != null) {
+ if (query.isExplain()) {
+ sessionConfig.out().flush();
+ return;
+ } else if (sessionConfig.isExecuteQuery() && compiled != null) {
GlobalConfig.ASTERIX_LOGGER.info(compiled.toJSON().toString(1));
JobId jobId = JobUtils.runJob(hcc, compiled, false);
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultPrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultPrinter.java
new file mode 100644
index 0000000..8e16ef7
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultPrinter.java
@@ -0,0 +1,188 @@
+/*
+ * 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.result;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+
+import org.apache.asterix.api.common.SessionConfig;
+import org.apache.asterix.common.utils.JSONUtil;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
+import org.apache.hyracks.api.comm.IFrame;
+import org.apache.hyracks.api.comm.IFrameTupleAccessor;
+import org.apache.hyracks.api.comm.VSizeFrame;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.control.nc.resources.memory.FrameManager;
+
+public class ResultPrinter {
+
+ // TODO(tillw): Should this be static?
+ private static FrameManager resultDisplayFrameMgr = new FrameManager(ResultReader.FRAME_SIZE);
+
+ private final SessionConfig conf;
+ private final ResultUtils.Stats stats;
+ private final ARecordType recordType;
+
+ private boolean indentJSON;
+ private boolean quoteRecord;
+
+ // Whether we are wrapping the output sequence in an array
+ private boolean wrapArray = false;
+ // Whether this is the first instance being output
+ private boolean notFirst = false;
+
+ public ResultPrinter(SessionConfig conf, ResultUtils.Stats stats, ARecordType recordType) {
+ this.conf = conf;
+ this.stats = stats;
+ this.recordType = recordType;
+ this.indentJSON = conf.is(SessionConfig.FORMAT_INDENT_JSON);
+ this.quoteRecord = conf.is(SessionConfig.FORMAT_QUOTE_RECORD);
+ }
+
+ private static void appendCSVHeader(Appendable app, ARecordType recordType) throws HyracksDataException {
+ try {
+ String[] fieldNames = recordType.getFieldNames();
+ boolean notfirst = false;
+ for (String name : fieldNames) {
+ if (notfirst) {
+ app.append(',');
+ }
+ notfirst = true;
+ app.append('"').append(name.replace("\"", "\"\"")).append('"');
+ }
+ app.append("\r\n");
+ } catch (IOException e) {
+ throw new HyracksDataException(e);
+ }
+ }
+
+ private void printPrefix() throws HyracksDataException {
+ // If we're outputting CSV with a header, the HTML header was already
+ // output by displayCSVHeader(), so skip it here
+ if (conf.is(SessionConfig.FORMAT_HTML)) {
+ conf.out().println("<h4>Results:</h4>");
+ conf.out().println("<pre>");
+ }
+
+ try {
+ conf.resultPrefix(new AlgebricksAppendable(conf.out()));
+ } catch (AlgebricksException e) {
+ throw new HyracksDataException(e);
+ }
+
+ if (conf.is(SessionConfig.FORMAT_WRAPPER_ARRAY)) {
+ conf.out().print("[ ");
+ wrapArray = true;
+ }
+
+ if (conf.fmt() == SessionConfig.OutputFormat.CSV && conf.is(SessionConfig.FORMAT_CSV_HEADER)) {
+ if (recordType == null) {
+ throw new HyracksDataException("Cannot print CSV with header without specifying output-record-type");
+ }
+ if (quoteRecord) {
+ StringWriter sw = new StringWriter();
+ appendCSVHeader(sw, recordType);
+ conf.out().print(JSONUtil.quoteAndEscape(sw.toString()));
+ conf.out().print("\n");
+ notFirst = true;
+ } else {
+ appendCSVHeader(conf.out(), recordType);
+ }
+ }
+ }
+
+ private void printPostfix() throws HyracksDataException {
+ conf.out().flush();
+ if (wrapArray) {
+ conf.out().println(" ]");
+ }
+ try {
+ conf.resultPostfix(new AlgebricksAppendable(conf.out()));
+ } catch (AlgebricksException e) {
+ throw new HyracksDataException(e);
+ }
+ if (conf.is(SessionConfig.FORMAT_HTML)) {
+ conf.out().println("</pre>");
+ }
+ }
+
+ private void displayRecord(String result) {
+ String record = result;
+ if (indentJSON) {
+ // TODO(tillw): this is inefficient - do this during record generation
+ record = JSONUtil.indent(record, 2);
+ }
+ if (conf.fmt() == SessionConfig.OutputFormat.CSV) {
+ // TODO(tillw): this is inefficient as well
+ record = record + "\r\n";
+ }
+ if (quoteRecord) {
+ // TODO(tillw): this is inefficient as well
+ record = JSONUtil.quoteAndEscape(record);
+ }
+ conf.out().print(record);
+ ++stats.count;
+ // TODO(tillw) fix this approximation
+ stats.size += record.length();
+ }
+
+ public void print(String record) throws HyracksDataException {
+ printPrefix();
+ // TODO(tillw) evil hack
+ quoteRecord = true;
+ displayRecord(record);
+ printPostfix();
+ }
+
+ public void print(ResultReader resultReader) throws HyracksDataException {
+ printPrefix();
+
+ final IFrameTupleAccessor fta = resultReader.getFrameTupleAccessor();
+ final IFrame frame = new VSizeFrame(resultDisplayFrameMgr);
+
+ while (resultReader.read(frame) > 0) {
+ final ByteBuffer frameBuffer = frame.getBuffer();
+ final byte[] frameBytes = frameBuffer.array();
+ fta.reset(frameBuffer);
+ final int last = fta.getTupleCount();
+ for (int tIndex = 0; tIndex < last; tIndex++) {
+ final int start = fta.getTupleStartOffset(tIndex);
+ int length = fta.getTupleEndOffset(tIndex) - start;
+ if (conf.fmt() == SessionConfig.OutputFormat.CSV
+ && ((length > 0) && (frameBytes[start + length - 1] == '\n'))) {
+ length--;
+ }
+ String result = new String(frameBytes, start, length, UTF_8);
+ if (wrapArray && notFirst) {
+ conf.out().print(", ");
+ }
+ notFirst = true;
+ displayRecord(result);
+ }
+ frameBuffer.clear();
+ }
+
+ printPostfix();
+ }
+}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultUtils.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultUtils.java
index 8c3ccfc..3503549 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultUtils.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/result/ResultUtils.java
@@ -24,7 +24,6 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
@@ -32,25 +31,16 @@
import java.util.regex.Pattern;
import org.apache.asterix.api.common.SessionConfig;
-import org.apache.asterix.api.common.SessionConfig.OutputFormat;
import org.apache.asterix.api.http.servlet.APIServlet;
-import org.apache.asterix.common.utils.JSONUtil;
import org.apache.asterix.om.types.ARecordType;
import org.apache.http.ParseException;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
-import org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable;
-import org.apache.hyracks.api.comm.IFrame;
-import org.apache.hyracks.api.comm.IFrameTupleAccessor;
-import org.apache.hyracks.api.comm.VSizeFrame;
import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.control.nc.resources.memory.FrameManager;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class ResultUtils {
- private static final Charset UTF_8 = Charset.forName("UTF-8");
-
static Map<Character, String> HTML_ENTITIES = new HashMap<Character, String>();
static {
@@ -74,124 +64,14 @@
return s;
}
- private static void printCSVHeader(ARecordType recordType, PrintWriter out) {
- String[] fieldNames = recordType.getFieldNames();
- boolean notfirst = false;
- for (String name : fieldNames) {
- if (notfirst) {
- out.print(',');
- }
- notfirst = true;
- out.print('"');
- out.print(name.replace("\"", "\"\""));
- out.print('"');
- }
- out.print("\r\n");
- }
-
- public static FrameManager resultDisplayFrameMgr = new FrameManager(ResultReader.FRAME_SIZE);
-
public static void displayResults(ResultReader resultReader, SessionConfig conf, Stats stats,
ARecordType recordType) throws HyracksDataException {
- // Whether we are wrapping the output sequence in an array
- boolean wrap_array = false;
- // Whether this is the first instance being output
- boolean notfirst = false;
+ new ResultPrinter(conf, stats, recordType).print(resultReader);
+ }
- // If we're outputting CSV with a header, the HTML header was already
- // output by displayCSVHeader(), so skip it here
- if (conf.is(SessionConfig.FORMAT_HTML)) {
- conf.out().println("<h4>Results:</h4>");
- conf.out().println("<pre>");
- }
-
- try {
- conf.resultPrefix(new AlgebricksAppendable(conf.out()));
- } catch (AlgebricksException e) {
- throw new HyracksDataException(e);
- }
-
- if (conf.is(SessionConfig.FORMAT_WRAPPER_ARRAY)) {
- conf.out().print("[ ");
- wrap_array = true;
- }
-
- final boolean indentJSON = conf.is(SessionConfig.FORMAT_INDENT_JSON);
- final boolean quoteRecord = conf.is(SessionConfig.FORMAT_QUOTE_RECORD);
-
- if (conf.fmt() == OutputFormat.CSV && conf.is(SessionConfig.FORMAT_CSV_HEADER)) {
- if (recordType == null) {
- throw new HyracksDataException("Cannot print CSV with header without specifying output-record-type");
- }
- if (quoteRecord) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- printCSVHeader(recordType, pw);
- pw.close();
- conf.out().print(JSONUtil.quoteAndEscape(sw.toString()));
- conf.out().print("\n");
- notfirst = true;
- } else {
- printCSVHeader(recordType, conf.out());
- }
- }
-
- final IFrameTupleAccessor fta = resultReader.getFrameTupleAccessor();
- final IFrame frame = new VSizeFrame(resultDisplayFrameMgr);
-
- while (resultReader.read(frame) > 0) {
- final ByteBuffer frameBuffer = frame.getBuffer();
- final byte[] frameBytes = frameBuffer.array();
- fta.reset(frameBuffer);
- final int last = fta.getTupleCount();
- for (int tIndex = 0; tIndex < last; tIndex++) {
- final int start = fta.getTupleStartOffset(tIndex);
- int length = fta.getTupleEndOffset(tIndex) - start;
- if (conf.fmt() == OutputFormat.CSV) {
- if ((length > 0) && (frameBytes[start + length - 1] == '\n')) {
- length--;
- }
- }
- String result = new String(frameBytes, start, length, UTF_8);
- if (wrap_array && notfirst) {
- conf.out().print(", ");
- }
- notfirst = true;
- if (indentJSON) {
- // TODO(tillw): this is inefficient - do this during result generation
- result = JSONUtil.indent(result, 2);
- }
- if (conf.fmt() == OutputFormat.CSV) {
- // TODO(tillw): this is inefficient as well
- result = result + "\r\n";
- }
- if (quoteRecord) {
- // TODO(tillw): this is inefficient as well
- result = JSONUtil.quoteAndEscape(result);
- }
- conf.out().print(result);
- ++stats.count;
- // TODO(tillw) fix this approximation
- stats.size += result.length();
- }
- frameBuffer.clear();
- }
-
- conf.out().flush();
-
- if (wrap_array) {
- conf.out().println(" ]");
- }
-
- try {
- conf.resultPostfix(new AlgebricksAppendable(conf.out()));
- } catch (AlgebricksException e) {
- throw new HyracksDataException(e);
- }
-
- if (conf.is(SessionConfig.FORMAT_HTML)) {
- conf.out().println("</pre>");
- }
+ public static void displayResults(String record, SessionConfig conf, Stats stats, ARecordType recordType)
+ throws HyracksDataException {
+ new ResultPrinter(conf, stats, recordType).print(record);
}
public static JSONObject getErrorResponse(int errorCode, String errorMessage, String errorSummary,
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/explain/explain_simple/explain_simple.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/explain/explain_simple/explain_simple.1.query.sqlpp
new file mode 100644
index 0000000..6860087
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/explain/explain_simple/explain_simple.1.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * 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 : EXPLAIN a plan for a very simple query
+* Expected Res : Success
+* Date : Jul 25, 2016
+*/
+explain select value 1+1;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/explain/explain_simple/explain_simple.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/explain/explain_simple/explain_simple.1.adm
new file mode 100644
index 0000000..d3b66a5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/explain/explain_simple/explain_simple.1.adm
@@ -0,0 +1,9 @@
+distribute result [%0->$$2]
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ assign [$$2] <- [AInt64: {2}]
+ -- ASSIGN |UNPARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |UNPARTITIONED|
+
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 99e4935..fa29a42 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -63,6 +63,13 @@
</compilation-unit>
</test-case>
</test-group>
+ <test-group name="explain">
+ <test-case FilePath="explain">
+ <compilation-unit name="explain_simple">
+ <output-dir compare="Text">explain_simple</output-dir>
+ </compilation-unit>
+ </test-case>
+ </test-group>
<!--
<test-group name="union">
<test-case FilePath="union">
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/statement/SubscribeFeedStatement.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/statement/SubscribeFeedStatement.java
index 9e6f857..836de6a 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/statement/SubscribeFeedStatement.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/statement/SubscribeFeedStatement.java
@@ -70,7 +70,7 @@
}
public void initialize(MetadataTransactionContext mdTxnCtx) throws MetadataException {
- this.query = new Query();
+ this.query = new Query(false);
EntityId sourceFeedId = connectionRequest.getFeedJointKey().getFeedId();
Feed subscriberFeed =
MetadataManager.INSTANCE.getFeed(mdTxnCtx, connectionRequest.getReceivingFeedId().getDataverse(),
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AqlDeleteRewriteVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AqlDeleteRewriteVisitor.java
index cedeb77..9268422 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AqlDeleteRewriteVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/AqlDeleteRewriteVisitor.java
@@ -64,7 +64,7 @@
VariableExpr returnExpr = new VariableExpr(var.getVar());
returnExpr.setIsNewVar(false);
FLWOGRExpression flowgr = new FLWOGRExpression(clauseList, returnExpr);
- Query query = new Query();
+ Query query = new Query(false);
query.setBody(flowgr);
deleteStmt.setQuery(query);
return null;
diff --git a/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj b/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
index 172e534..b3f6200 100644
--- a/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
+++ b/asterixdb/asterix-lang-aql/src/main/javacc/AQL.jj
@@ -1528,7 +1528,7 @@
Query Query() throws ParseException:
{
- Query query = new Query();
+ Query query = new Query(false);
// we set the pointers to the dataverses and datasets lists to fill them with entities to be locked
setDataverses(query.getDataverses());
setDatasets(query.getDatasets());
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java
index 80853c8..9be4830 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java
@@ -28,17 +28,20 @@
import org.apache.commons.lang3.ObjectUtils;
public class Query implements Statement {
+ private final boolean explain;
private boolean topLevel = true;
private Expression body;
private int varCounter;
private List<String> dataverses = new ArrayList<>();
private List<String> datasets = new ArrayList<>();
- public Query() {
- // Default constructor.
+ public Query(boolean explain) {
+ this.explain = explain;
}
- public Query(boolean topLevel, Expression body, int varCounter, List<String> dataverses, List<String> datasets) {
+ public Query(boolean explain, boolean topLevel, Expression body, int varCounter, List<String> dataverses,
+ List<String> datasets) {
+ this.explain = explain;
this.topLevel = topLevel;
this.body = body;
this.varCounter = varCounter;
@@ -70,6 +73,10 @@
return topLevel;
}
+ public boolean isExplain() {
+ return explain;
+ }
+
@Override
public byte getKind() {
return Statement.Kind.QUERY;
@@ -98,7 +105,7 @@
@Override
public int hashCode() {
- return ObjectUtils.hashCodeMulti(body, datasets, dataverses, topLevel);
+ return ObjectUtils.hashCodeMulti(body, datasets, dataverses, topLevel, explain);
}
@Override
@@ -110,7 +117,8 @@
return false;
}
Query target = (Query) object;
- return ObjectUtils.equals(body, target.body) && ObjectUtils.equals(datasets, target.datasets)
- && ObjectUtils.equals(dataverses, target.dataverses) && topLevel == target.topLevel;
+ return explain == target.explain && ObjectUtils.equals(body, target.body)
+ && ObjectUtils.equals(datasets, target.datasets) && ObjectUtils.equals(dataverses, target.dataverses)
+ && topLevel == target.topLevel;
}
}
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
index 159af9f..f79f811 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
@@ -307,7 +307,7 @@
}
protected Expression rewriteFunctionBody(Expression expr) throws AsterixException {
- Query wrappedQuery = new Query();
+ Query wrappedQuery = new Query(false);
wrappedQuery.setBody(expr);
wrappedQuery.setTopLevel(false);
IQueryRewriter queryRewriter = rewriterFactory.createQueryRewriter();
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
index 667878c..9ffcf8d 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
@@ -235,7 +235,7 @@
@Override
public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(Query q, VariableSubstitutionEnvironment env)
throws AsterixException {
- Query newQ = new Query();
+ Query newQ = new Query(q.isExplain());
Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = q.getBody().accept(this, env);
newQ.setBody((Expression) p1.first);
return new Pair<>(newQ, p1.second);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index 01f3524..d752396 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -229,7 +229,7 @@
@Override
public Query visit(Query q, Void arg) throws AsterixException {
- return new Query(q.isTopLevel(), (Expression) q.getBody().accept(this, arg), q.getVarCounter(),
+ return new Query(q.isExplain(), q.isTopLevel(), (Expression) q.getBody().accept(this, arg), q.getVarCounter(),
q.getDataverses(), q.getDatasets());
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java
index bfb1e44..a71cc47 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java
@@ -84,7 +84,7 @@
SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, null, whereClause, null, null, null);
SelectSetOperation selectSetOperation = new SelectSetOperation(new SetOperationInput(selectBlock, null), null);
SelectExpression selectExpression = new SelectExpression(null, selectSetOperation, null, null, false);
- Query query = new Query(false, selectExpression, 0, new ArrayList<>(), new ArrayList<>());
+ Query query = new Query(false, false, selectExpression, 0, new ArrayList<>(), new ArrayList<>());
query.setBody(selectExpression);
// return the delete statement.
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index afc970a..8f9a201 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -316,7 +316,8 @@
| stmt = UpdateStatement()
| stmt = FeedStatement()
| stmt = CompactStatement()
- | stmt = Query() <SEMICOLON>
+ | stmt = ExplainStatement()
+ | stmt = Query(false) <SEMICOLON>
| stmt = RefreshExternalDatasetStatement()
| stmt = RunStatement()
)
@@ -925,7 +926,7 @@
Query query;
}
{
- <INSERT> <INTO> nameComponents = QualifiedName() query = Query()
+ <INSERT> <INTO> nameComponents = QualifiedName() query = Query(false)
{
query.setTopLevel(true);
return new InsertStatement(nameComponents.first, nameComponents.second, query, getVarCounter());
@@ -1562,10 +1563,20 @@
}
}
-
-Query Query() throws ParseException:
+Query ExplainStatement() throws ParseException:
{
- Query query = new Query();
+ Query query;
+}
+{
+ "explain" query = Query(true)
+ {
+ return query;
+ }
+}
+
+Query Query(boolean explain) throws ParseException:
+{
+ Query query = new Query(explain);
// we set the pointers to the dataverses and datasets lists to fill them with entities to be locked
setDataverses(query.getDataverses());
setDatasets(query.getDatasets());
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
index 7c749ed..fe8e044 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
@@ -142,7 +142,7 @@
@Override
public Void visitDistinctOperator(DistinctOperator op, Integer indent) throws AlgebricksException {
- addIndent(indent).append("distinct " + "(");
+ addIndent(indent).append("distinct (");
pprintExprList(op.getExpressions(), indent);
buffer.append(")");
return null;
@@ -177,7 +177,6 @@
}
String fst = getOrderString(p.first);
buffer.append("(" + fst + ", " + p.second.getValue().accept(exprVisitor, indent) + ") ");
-
}
return null;
}
@@ -346,7 +345,7 @@
@Override
public Void visitExchangeOperator(ExchangeOperator op, Integer indent) throws AlgebricksException {
- addIndent(indent).append("exchange ");
+ addIndent(indent).append("exchange");
return null;
}
@@ -358,13 +357,13 @@
@Override
public Void visitReplicateOperator(ReplicateOperator op, Integer indent) throws AlgebricksException {
- addIndent(indent).append("replicate ");
+ addIndent(indent).append("replicate");
return null;
}
@Override
public Void visitMaterializeOperator(MaterializeOperator op, Integer indent) throws AlgebricksException {
- addIndent(indent).append("materialize ");
+ addIndent(indent).append("materialize");
return null;
}