You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by ji...@apache.org on 2015/03/24 09:14:24 UTC
tajo git commit: TAJO-1426: Support "explain global" to get physical
plan.
Repository: tajo
Updated Branches:
refs/heads/master d7b5212ce -> 3e9a2dd2b
TAJO-1426: Support "explain global" to get physical plan.
Closes #441
Signed-off-by: Jihoon Son <ji...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/tajo/repo
Commit: http://git-wip-us.apache.org/repos/asf/tajo/commit/3e9a2dd2
Tree: http://git-wip-us.apache.org/repos/asf/tajo/tree/3e9a2dd2
Diff: http://git-wip-us.apache.org/repos/asf/tajo/diff/3e9a2dd2
Branch: refs/heads/master
Commit: 3e9a2dd2ba86653ec7b90394518a1a700bd937dc
Parents: d7b5212
Author: Jihoon Son <ji...@apache.org>
Authored: Tue Mar 24 17:12:24 2015 +0900
Committer: Jihoon Son <ji...@apache.org>
Committed: Tue Mar 24 17:13:57 2015 +0900
----------------------------------------------------------------------
CHANGES | 3 +
.../java/org/apache/tajo/algebra/Explain.java | 9 +-
.../main/java/org/apache/tajo/SessionVars.java | 1 +
.../java/org/apache/tajo/conf/TajoConf.java | 1 +
.../java/org/apache/tajo/util/FileUtil.java | 25 ++-
.../org/apache/tajo/engine/parser/SQLLexer.g4 | 1 +
.../org/apache/tajo/engine/parser/SQLParser.g4 | 2 +-
.../apache/tajo/engine/parser/SQLAnalyzer.java | 2 +-
.../engine/planner/global/GlobalPlanner.java | 4 +
.../ExplainGlobalPlanPreprocessorForTest.java | 62 ++++++
.../exec/ExplainPlanPreprocessorForTest.java | 218 +++++++++++++++++++
.../apache/tajo/master/exec/QueryExecutor.java | 58 ++++-
.../tajo/querymaster/QueryMasterTask.java | 1 +
.../java/org/apache/tajo/QueryTestCaseBase.java | 75 ++++++-
.../tajo/engine/query/TestSelectQuery.java | 16 ++
.../testExplainSelectPhysical.1.result | 26 +++
.../testExplainSelectPhysical.2.result | 88 ++++++++
.../testExplainSelectPhysical.3.result | 89 ++++++++
.../java/org/apache/tajo/plan/LogicalPlan.java | 17 +-
.../org/apache/tajo/plan/LogicalPlanner.java | 2 +-
20 files changed, 681 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 58c061a..f573550 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,9 @@ Release 0.11.0 - unreleased
IMPROVEMENT
+ TAJO-1426: Support "explain global" to get physical plan. (Contributed by
+ navis, Committed by jihoon)
+
TAJO-1407: Minor performance improvement of MemSortExec. (Contributed by
navis, Committed by jihoon)
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-algebra/src/main/java/org/apache/tajo/algebra/Explain.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Explain.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Explain.java
index ee76ea9..2e966b4 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Explain.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Explain.java
@@ -22,11 +22,18 @@ import com.google.common.base.Objects;
public class Explain extends UnaryOperator {
- public Explain(Expr operand) {
+ private boolean isGlobal;
+
+ public Explain(Expr operand, boolean isGlobal) {
super(OpType.Explain);
+ this.isGlobal = isGlobal;
setChild(operand);
}
+ public boolean isGlobal() {
+ return isGlobal;
+ }
+
public int hashCode() {
return Objects.hashCode(getChild());
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-common/src/main/java/org/apache/tajo/SessionVars.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/SessionVars.java b/tajo-common/src/main/java/org/apache/tajo/SessionVars.java
index b3233ed..5cca413 100644
--- a/tajo-common/src/main/java/org/apache/tajo/SessionVars.java
+++ b/tajo-common/src/main/java/org/apache/tajo/SessionVars.java
@@ -139,6 +139,7 @@ public enum SessionVars implements ConfigKey {
TEST_JOIN_OPT_ENABLED(ConfVars.$TEST_JOIN_OPT_ENABLED, "(test only) join optimization enabled", TEST_VAR),
TEST_FILTER_PUSHDOWN_ENABLED(ConfVars.$TEST_FILTER_PUSHDOWN_ENABLED, "filter push down enabled", TEST_VAR),
TEST_MIN_TASK_NUM(ConfVars.$TEST_MIN_TASK_NUM, "(test only) min task num", TEST_VAR),
+ TEST_PLAN_SHAPE_FIX_ENABLED(ConfVars.$TEST_PLAN_SHAPE_FIX_ENABLED, "(test only) plan shape fix enabled", TEST_VAR),
;
public static final Map<String, SessionVars> SESSION_VARS = Maps.newHashMap();
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java b/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
index 5b569d5..ecdb2ef 100644
--- a/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
+++ b/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
@@ -355,6 +355,7 @@ public class TajoConf extends Configuration {
$TEST_JOIN_OPT_ENABLED("tajo.test.plan.join-optimization.enabled", true),
$TEST_FILTER_PUSHDOWN_ENABLED("tajo.test.plan.filter-pushdown.enabled", true),
$TEST_MIN_TASK_NUM("tajo.test.min-task-num", -1),
+ $TEST_PLAN_SHAPE_FIX_ENABLED("tajo.test.plan.shape.fix.enabled", false), // used for explain statement test
// Behavior Control ---------------------------------------------------------
$BEHAVIOR_ARITHMETIC_ABORT("tajo.behavior.arithmetic-abort", false),
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-common/src/main/java/org/apache/tajo/util/FileUtil.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/util/FileUtil.java b/tajo-common/src/main/java/org/apache/tajo/util/FileUtil.java
index 9aa6af9..9403a2f 100644
--- a/tajo-common/src/main/java/org/apache/tajo/util/FileUtil.java
+++ b/tajo-common/src/main/java/org/apache/tajo/util/FileUtil.java
@@ -87,19 +87,23 @@ public class FileUtil {
}
public static String readTextFileFromResource(String resource) throws IOException {
- StringBuilder fileData = new StringBuilder(1000);
- InputStream inputStream = ClassLoader.getSystemResourceAsStream(resource);
- byte[] buf = new byte[1024];
- int numRead;
+ return readTextFromStream(ClassLoader.getSystemResourceAsStream(resource));
+ }
+
+ public static String readTextFromStream(InputStream inputStream)
+ throws IOException {
try {
+ StringBuilder fileData = new StringBuilder(1000);
+ byte[] buf = new byte[1024];
+ int numRead;
while ((numRead = inputStream.read(buf)) != -1) {
String readData = new String(buf, 0, numRead, Charset.defaultCharset());
fileData.append(readData);
}
+ return fileData.toString();
} finally {
- IOUtils.cleanup(null, inputStream);
+ IOUtils.closeStream(inputStream);
}
- return fileData.toString();
}
public static String readTextFile(File file) throws IOException {
@@ -119,6 +123,15 @@ public class FileUtil {
return fileData.toString();
}
+ public static void writeTextToStream(String text, OutputStream outputStream)
+ throws IOException {
+ try {
+ outputStream.write(text.getBytes());
+ } finally {
+ IOUtils.closeStream(outputStream);
+ }
+ }
+
public static String humanReadableByteCount(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit) return bytes + " B";
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4 b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
index f42e114..6fccaad 100644
--- a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
+++ b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
@@ -237,6 +237,7 @@ FOLLOWING : F O L L O W I N G;
FORMAT : F O R M A T;
FUSION : F U S I O N;
+GLOBAL : G L O B A L;
GROUPING : G R O U P I N G;
HASH : H A S H;
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4 b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
index a05a060..9ac3f8c 100644
--- a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
+++ b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
@@ -39,7 +39,7 @@ sql
;
explain_clause
- : EXPLAIN
+ : EXPLAIN (GLOBAL)?
;
statement
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
index 869c0eb..23c2eec 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
@@ -78,7 +78,7 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
public Expr visitSql(SqlContext ctx) {
Expr statement = visit(ctx.statement());
if (checkIfExist(ctx.explain_clause())) {
- return new Explain(statement);
+ return new Explain(statement, checkIfExist(ctx.explain_clause().GLOBAL()));
} else {
return statement;
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
index d2ac6cc..cd35d96 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
@@ -91,6 +91,10 @@ public class GlobalPlanner {
this(conf, workerContext.getCatalog());
}
+ public TajoConf getConf() {
+ return conf;
+ }
+
public CatalogService getCatalog() {
return catalog;
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainGlobalPlanPreprocessorForTest.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainGlobalPlanPreprocessorForTest.java b/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainGlobalPlanPreprocessorForTest.java
new file mode 100644
index 0000000..c26e12c
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainGlobalPlanPreprocessorForTest.java
@@ -0,0 +1,62 @@
+/**
+ * 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.tajo.master.exec;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.engine.planner.global.DataChannel;
+import org.apache.tajo.engine.planner.global.ExecutionBlock;
+import org.apache.tajo.engine.planner.global.ExecutionBlockCursor;
+import org.apache.tajo.engine.planner.global.MasterPlan;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Data channels of a global plan can have multiple shuffle keys, and their appearance order is basically not preserved.
+ * However, to test the equivalence of global plans, the appearance order of shuffle keys must be preserved.
+ * This class guarantees the consistency of the order of shuffle keys.
+ */
+public class ExplainGlobalPlanPreprocessorForTest {
+ private static final ExplainPlanPreprocessorForTest.ColumnComparator columnComparator =
+ new ExplainPlanPreprocessorForTest.ColumnComparator();
+
+ /**
+ * For all data channels, sort shuffle keys by their names.
+ *
+ * @param plan master plan
+ */
+ public void prepareTest(MasterPlan plan) {
+ ExecutionBlockCursor cursor = new ExecutionBlockCursor(plan);
+
+ while (cursor.hasNext()) {
+ ExecutionBlock block = cursor.nextBlock();
+ List<DataChannel> outgoingChannels = plan.getOutgoingChannels(block.getId());
+ if (outgoingChannels != null) {
+ for (DataChannel channel : outgoingChannels) {
+ if (channel.hasShuffleKeys()) {
+ Column[] shuffleKeys = channel.getShuffleKeys();
+ Arrays.sort(shuffleKeys, columnComparator);
+ channel.setShuffleKeys(shuffleKeys);
+ }
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainPlanPreprocessorForTest.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainPlanPreprocessorForTest.java b/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainPlanPreprocessorForTest.java
new file mode 100644
index 0000000..ab37e22
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/master/exec/ExplainPlanPreprocessorForTest.java
@@ -0,0 +1,218 @@
+/**
+ * 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.tajo.master.exec;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.plan.LogicalPlan;
+import org.apache.tajo.plan.PlanningException;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.AlgebraicUtil;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.plan.logical.JoinNode;
+import org.apache.tajo.plan.logical.LogicalNode;
+import org.apache.tajo.plan.logical.ScanNode;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.visitor.BasicLogicalPlanVisitor;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Stack;
+
+/**
+ * Tajo's logical planner can generate different shapes of logical plans for the same query,
+ * especially when the query involves one or more joins.
+ * This class guarantees the consistency of the logical plan for the same query.
+ */
+public class ExplainPlanPreprocessorForTest {
+ private static final PlanShapeFixerContext shapeFixerContext = new PlanShapeFixerContext();
+ private static final PlanShapeFixer shapeFixer = new PlanShapeFixer();
+ private static final PidResetContext resetContext = new PidResetContext();
+ private static final PidReseter pidReseter = new PidReseter();
+
+ public void prepareTest(LogicalPlan plan) throws PlanningException {
+ // Pid reseter
+ resetContext.reset();
+ pidReseter.visit(resetContext, plan, plan.getRootBlock());
+
+ // Plan shape fixer
+ shapeFixerContext.reset();
+ shapeFixer.visit(shapeFixerContext, plan, plan.getRootBlock());
+ }
+
+ private static class PlanShapeFixerContext {
+
+ Stack<Integer> childNumbers = new Stack<Integer>();
+ public void reset() {
+ childNumbers.clear();
+ }
+ }
+
+ /**
+ * Given a commutative join, two children of the join node are interchangeable.
+ * This class fix the logical plan according to the following rules.
+ *
+ * <h3>Rules</h3>
+ * <ul>
+ * <li>When one of the both children has more descendants,
+ * change the plan in order that the left child is the one who has more descendants.</li>
+ * <li>When both children have the same number of descendants,
+ * their order is decided based on their string representation.</li>
+ * </ul>
+ *
+ * In addition, in/out schemas, quals, and targets are sorted by their names.
+ */
+ private static class PlanShapeFixer extends BasicLogicalPlanVisitor<PlanShapeFixerContext, LogicalNode> {
+ private static final ColumnComparator columnComparator = new ColumnComparator();
+ private static final EvalNodeComparator evalNodeComparator = new EvalNodeComparator();
+ private static final TargetComparator targetComparator = new TargetComparator();
+
+ @Override
+ public LogicalNode visit(PlanShapeFixerContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ LogicalNode node, Stack<LogicalNode> stack) throws PlanningException {
+ super.visit(context, plan, block, node, stack);
+ context.childNumbers.push(context.childNumbers.pop()+1);
+ return null;
+ }
+
+ @Override
+ public LogicalNode visitScan(PlanShapeFixerContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ ScanNode node, Stack<LogicalNode> stack) throws PlanningException {
+ super.visitScan(context, plan, block, node, stack);
+ context.childNumbers.push(1);
+ node.setInSchema(sortSchema(node.getInSchema()));
+ if (node.hasQual()) {
+ node.setQual(sortQual(node.getQual()));
+ }
+ return null;
+ }
+
+ @Override
+ public LogicalNode visitJoin(PlanShapeFixerContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ JoinNode node, Stack<LogicalNode> stack) throws PlanningException {
+ super.visitJoin(context, plan, block, node, stack);
+ int rightChildNum = context.childNumbers.pop();
+ int leftChildNum = context.childNumbers.pop();
+
+ if (PlannerUtil.isCommutativeJoin(node.getJoinType())) {
+
+ if (leftChildNum < rightChildNum) {
+ swapChildren(node);
+ } else if (leftChildNum == rightChildNum) {
+ if (node.getLeftChild().toString().compareTo(node.getRightChild().toString()) <
+ 0) {
+ swapChildren(node);
+ }
+ }
+ }
+
+ node.setInSchema(sortSchema(node.getInSchema()));
+ node.setOutSchema(sortSchema(node.getOutSchema()));
+
+ if (node.hasJoinQual()) {
+ node.setJoinQual(sortQual(node.getJoinQual()));
+ }
+
+ if (node.hasTargets()) {
+ node.setTargets(sortTargets(node.getTargets()));
+ }
+
+ context.childNumbers.push(rightChildNum + leftChildNum);
+
+ return null;
+ }
+
+ private Schema sortSchema(Schema schema) {
+ Column[] columns = schema.toArray();
+ Arrays.sort(columns, columnComparator);
+
+ Schema sorted = new Schema();
+ for (Column col : columns) {
+ sorted.addColumn(col);
+ }
+ return sorted;
+ }
+
+ private EvalNode sortQual(EvalNode qual) {
+ EvalNode[] cnf = AlgebraicUtil.toConjunctiveNormalFormArray(qual);
+ Arrays.sort(cnf, evalNodeComparator);
+ return AlgebraicUtil.createSingletonExprFromCNF(cnf);
+ }
+
+ private Target[] sortTargets(Target[] targets) {
+ Arrays.sort(targets, targetComparator);
+ return targets;
+ }
+
+ private static void swapChildren(JoinNode node) {
+ LogicalNode tmpChild = node.getLeftChild();
+ int tmpId = tmpChild.getPID();
+ tmpChild.setPID(node.getRightChild().getPID());
+ node.getRightChild().setPID(tmpId);
+ node.setLeftChild(node.getRightChild());
+ node.setRightChild(tmpChild);
+ }
+ }
+
+ public static class ColumnComparator implements Comparator<Column> {
+
+ @Override
+ public int compare(Column o1, Column o2) {
+ return o1.getQualifiedName().compareTo(o2.getQualifiedName());
+ }
+ }
+
+ private static class EvalNodeComparator implements Comparator<EvalNode> {
+
+ @Override
+ public int compare(EvalNode o1, EvalNode o2) {
+ return o1.toJson().compareTo(o2.toJson());
+ }
+ }
+
+ private static class TargetComparator implements Comparator<Target> {
+
+ @Override
+ public int compare(Target o1, Target o2) {
+ return o1.toJson().compareTo(o2.toJson());
+ }
+ }
+
+ private static class PidResetContext {
+ int seqId = 0;
+ public void reset() {
+ seqId = 0;
+ }
+ }
+
+ /**
+ * During join order optimization, new join nodes are created based on the chosen join order.
+ * So, each join node has different pids.
+ * This class sequentially assigns unique pids to all logical nodes.
+ */
+ private static class PidReseter extends BasicLogicalPlanVisitor<PidResetContext, LogicalNode> {
+
+ @Override
+ public void preHook(LogicalPlan plan, LogicalNode node, Stack<LogicalNode> stack, PidResetContext context)
+ throws PlanningException {
+ node.setPID(context.seqId++);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/java/org/apache/tajo/master/exec/QueryExecutor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/master/exec/QueryExecutor.java b/tajo-core/src/main/java/org/apache/tajo/master/exec/QueryExecutor.java
index aa8b228..75e7762 100644
--- a/tajo-core/src/main/java/org/apache/tajo/master/exec/QueryExecutor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/master/exec/QueryExecutor.java
@@ -36,6 +36,8 @@ import org.apache.tajo.catalog.statistics.TableStats;
import org.apache.tajo.common.TajoDataTypes;
import org.apache.tajo.conf.TajoConf;
import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.engine.planner.global.GlobalPlanner;
+import org.apache.tajo.engine.planner.global.MasterPlan;
import org.apache.tajo.engine.planner.physical.EvalExprExec;
import org.apache.tajo.engine.planner.physical.StoreTableExec;
import org.apache.tajo.engine.query.QueryContext;
@@ -45,6 +47,7 @@ import org.apache.tajo.master.*;
import org.apache.tajo.master.exec.prehook.CreateTableHook;
import org.apache.tajo.master.exec.prehook.DistributedQueryHookManager;
import org.apache.tajo.master.exec.prehook.InsertIntoHook;
+import org.apache.tajo.plan.rewrite.LogicalPlanRewriteRule;
import org.apache.tajo.querymaster.*;
import org.apache.tajo.session.Session;
import org.apache.tajo.plan.LogicalPlan;
@@ -101,7 +104,7 @@ public class QueryExecutor {
} else if (plan.isExplain()) { // explain query
- execExplain(plan, response);
+ execExplain(plan, queryContext, plan.isExplainGlobal(), response);
} else if (PlannerUtil.checkIfQueryTargetIsVirtualTable(plan)) {
execQueryOnVirtualTable(queryContext, session, sql, plan, response);
@@ -157,9 +160,28 @@ public class QueryExecutor {
response.setResultCode(ClientProtos.ResultCode.OK);
}
- public void execExplain(LogicalPlan plan, SubmitQueryResponse.Builder response) throws IOException {
+ public void execExplain(LogicalPlan plan, QueryContext queryContext, boolean isGlobal,
+ SubmitQueryResponse.Builder response)
+ throws Exception {
+ String explainStr;
+ boolean isTest = queryContext.getBool(SessionVars.TEST_PLAN_SHAPE_FIX_ENABLED);
+ if (isTest) {
+ ExplainPlanPreprocessorForTest preprocessorForTest = new ExplainPlanPreprocessorForTest();
+ preprocessorForTest.prepareTest(plan);
+ }
+
+ if (isGlobal) {
+ GlobalPlanner planner = new GlobalPlanner(context.getConf(), context.getCatalog());
+ MasterPlan masterPlan = compileMasterPlan(plan, queryContext, planner);
+ if (isTest) {
+ ExplainGlobalPlanPreprocessorForTest globalPlanPreprocessorForTest = new ExplainGlobalPlanPreprocessorForTest();
+ globalPlanPreprocessorForTest.prepareTest(masterPlan);
+ }
+ explainStr = masterPlan.toString();
+ } else {
+ explainStr = PlannerUtil.buildExplainString(plan.getRootBlock().getRoot());
+ }
- String explainStr = PlannerUtil.buildExplainString(plan.getRootBlock().getRoot());
Schema schema = new Schema();
schema.addColumn("explain", TajoDataTypes.Type.TEXT);
RowStoreUtil.RowStoreEncoder encoder = RowStoreUtil.createEncoder(schema);
@@ -416,4 +438,34 @@ public class QueryExecutor {
" is forwarded to " + queryInfo.getQueryMasterHost() + ":" + queryInfo.getQueryMasterPort());
}
}
+
+ public static MasterPlan compileMasterPlan(LogicalPlan plan, QueryContext context, GlobalPlanner planner)
+ throws Exception {
+
+ CatalogProtos.StoreType storeType = PlannerUtil.getStoreType(plan);
+ if (storeType != null) {
+ StorageManager sm = StorageManager.getStorageManager(planner.getConf(), storeType);
+ StorageProperty storageProperty = sm.getStorageProperty();
+ if (storageProperty.isSortedInsert()) {
+ String tableName = PlannerUtil.getStoreTableName(plan);
+ LogicalRootNode rootNode = plan.getRootBlock().getRoot();
+ TableDesc tableDesc = PlannerUtil.getTableDesc(planner.getCatalog(), rootNode.getChild());
+ if (tableDesc == null) {
+ throw new VerifyException("Can't get table meta data from catalog: " + tableName);
+ }
+ List<LogicalPlanRewriteRule> storageSpecifiedRewriteRules = sm.getRewriteRules(
+ context, tableDesc);
+ if (storageSpecifiedRewriteRules != null) {
+ for (LogicalPlanRewriteRule eachRule: storageSpecifiedRewriteRules) {
+ eachRule.rewrite(context, plan);
+ }
+ }
+ }
+ }
+
+ MasterPlan masterPlan = new MasterPlan(QueryIdFactory.NULL_QUERY_ID, context, plan);
+ planner.build(masterPlan);
+
+ return masterPlan;
+ }
}
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/main/java/org/apache/tajo/querymaster/QueryMasterTask.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/querymaster/QueryMasterTask.java b/tajo-core/src/main/java/org/apache/tajo/querymaster/QueryMasterTask.java
index 0d1924b..f83cb1e 100644
--- a/tajo-core/src/main/java/org/apache/tajo/querymaster/QueryMasterTask.java
+++ b/tajo-core/src/main/java/org/apache/tajo/querymaster/QueryMasterTask.java
@@ -38,6 +38,7 @@ import org.apache.tajo.catalog.CatalogService;
import org.apache.tajo.catalog.TableDesc;
import org.apache.tajo.catalog.proto.CatalogProtos.StoreType;
import org.apache.tajo.conf.TajoConf;
+import org.apache.tajo.engine.planner.global.GlobalPlanner;
import org.apache.tajo.engine.planner.global.MasterPlan;
import org.apache.tajo.engine.query.QueryContext;
import org.apache.tajo.exception.UnimplementedException;
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java b/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
index 15fbdae..4e104dd 100644
--- a/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
+++ b/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
@@ -21,9 +21,12 @@ package org.apache.tajo;
import com.google.protobuf.ServiceException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.io.IOUtils;
import org.apache.tajo.algebra.*;
import org.apache.tajo.annotation.Nullable;
import org.apache.tajo.catalog.CatalogService;
@@ -50,10 +53,19 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestName;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@@ -181,6 +193,8 @@ public class QueryTestCaseBase {
protected Path currentResultPath;
protected Path currentDatasetPath;
+ protected FileSystem currentResultFS;
+
// for getting a method name
@Rule public TestName name = new TestName();
@@ -243,8 +257,10 @@ public class QueryTestCaseBase {
client.updateQuery("CREATE DATABASE IF NOT EXISTS " + CatalogUtil.denormalizeIdentifier(currentDatabase));
}
client.selectDatabase(currentDatabase);
- } catch (ServiceException e) {
- e.printStackTrace();
+ currentResultFS = currentResultPath.getFileSystem(testBase.getTestingCluster().getConfiguration());
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
testingCluster.setAllTajoDaemonConfValue(TajoConf.ConfVars.$TEST_BROADCAST_JOIN_ENABLED.varname, "false");
}
@@ -317,6 +333,61 @@ public class QueryTestCaseBase {
return executeFile(getMethodName() + ".sql");
}
+ private volatile Description current;
+
+ @Rule
+ public TestRule watcher = new TestWatcher() {
+ @Override
+ protected void starting(Description description) {
+ QueryTestCaseBase.this.current = description;
+ }
+ };
+
+ @Target({ElementType.METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ protected static @interface SimpleTest {
+ String[] queries();
+ String[] cleanupTables() default {};
+ }
+
+ protected void runSimpleTests() throws Exception {
+ String methodName = getMethodName();
+ Method method = current.getTestClass().getMethod(methodName);
+ SimpleTest annotation = method.getAnnotation(SimpleTest.class);
+ if (annotation == null) {
+ throw new IllegalStateException("Cannot find test annotation");
+ }
+ String[] queries = annotation.queries();
+ try {
+ for (int i = 0; i < queries.length; i++) {
+ ResultSet result = client.executeQueryAndGetResult(queries[i]);
+ Path resultPath = StorageUtil.concatPath(
+ currentResultPath, methodName + "." + String.valueOf(i + 1) + ".result");
+ if (currentResultFS.exists(resultPath)) {
+ assertEquals("Result Verification for: " + (i+1) + "th test",
+ FileUtil.readTextFromStream(currentResultFS.open(resultPath)), resultSetToString(result).trim());
+ } else if (!isNull(result)) {
+ // If there is no result file expected, create gold files for new tests.
+ FileUtil.writeTextToStream(resultSetToString(result).trim(), currentResultFS.create(resultPath));
+ LOG.info("New test output for " + current.getDisplayName() + " is written to " + resultPath);
+ // should be copied to src directory
+ }
+ }
+ } finally {
+ for (String tableName : annotation.cleanupTables()) {
+ try {
+ client.dropTable(tableName);
+ } catch (ServiceException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ private boolean isNull(ResultSet result) throws SQLException {
+ return result.getMetaData().getColumnCount() == 0;
+ }
+
protected String getMethodName() {
String methodName = name.getMethodName();
// In the case of parameter execution name's pattern is methodName[0]
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/test/java/org/apache/tajo/engine/query/TestSelectQuery.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestSelectQuery.java b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestSelectQuery.java
index f7b1382..b54d7ea 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestSelectQuery.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestSelectQuery.java
@@ -105,6 +105,22 @@ public class TestSelectQuery extends QueryTestCaseBase {
}
@Test
+ @SimpleTest(queries = {
+ "explain global " +
+ "select l_orderkey, l_partkey from lineitem",
+ "explain global " +
+ "select n1.n_nationkey, n1.n_name, n2.n_name from nation n1 join nation n2 on n1.n_name = upper(n2.n_name) " +
+ "order by n1.n_nationkey;",
+ "explain global " +
+ "select l_linenumber, count(*), count(distinct l_orderkey), sum(distinct l_orderkey) from lineitem " +
+ "group by l_linenumber having sum(distinct l_orderkey) = 6"})
+ public final void testExplainSelectPhysical() throws Exception {
+ // Enable this option to fix the shape of the generated plans.
+ testingCluster.getConfiguration().set(ConfVars.$TEST_PLAN_SHAPE_FIX_ENABLED.varname, "true");
+ runSimpleTests();
+ }
+
+ @Test
public final void testSelect() throws Exception {
// select l_orderkey, l_partkey from lineitem;
ResultSet res = executeQuery();
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.1.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.1.result b/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.1.result
new file mode 100644
index 0000000..0069639
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.1.result
@@ -0,0 +1,26 @@
+explain
+-------------------------------
+-------------------------------------------------------------------------------
+Execution Block Graph (TERMINAL - eb_0000000000000_0000_000002)
+-------------------------------------------------------------------------------
+|-eb_0000000000000_0000_000002
+ |-eb_0000000000000_0000_000001
+-------------------------------------------------------------------------------
+Order of Execution
+-------------------------------------------------------------------------------
+1: eb_0000000000000_0000_000001
+2: eb_0000000000000_0000_000002
+-------------------------------------------------------------------------------
+
+=======================================================
+Block Id: eb_0000000000000_0000_000001 [ROOT]
+=======================================================
+
+SCAN(0) on default.lineitem
+ => target list: default.lineitem.l_orderkey (INT4), default.lineitem.l_partkey (INT4)
+ => out schema: {(2) default.lineitem.l_orderkey (INT4), default.lineitem.l_partkey (INT4)}
+ => in schema: {(16) default.lineitem.l_comment (TEXT), default.lineitem.l_commitdate (TEXT), default.lineitem.l_discount (FLOAT8), default.lineitem.l_extendedprice (FLOAT8), default.lineitem.l_linenumber (INT4), default.lineitem.l_linestatus (TEXT), default.lineitem.l_orderkey (INT4), default.lineitem.l_partkey (INT4), default.lineitem.l_quantity (FLOAT8), default.lineitem.l_receiptdate (TEXT), default.lineitem.l_returnflag (TEXT), default.lineitem.l_shipdate (TEXT), default.lineitem.l_shipinstruct (TEXT), default.lineitem.l_shipmode (TEXT), default.lineitem.l_suppkey (INT4), default.lineitem.l_tax (FLOAT8)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000002 [TERMINAL]
+=======================================================
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.2.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.2.result b/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.2.result
new file mode 100644
index 0000000..7946c5b
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.2.result
@@ -0,0 +1,88 @@
+explain
+-------------------------------
+-------------------------------------------------------------------------------
+Execution Block Graph (TERMINAL - eb_0000000000000_0000_000005)
+-------------------------------------------------------------------------------
+|-eb_0000000000000_0000_000005
+ |-eb_0000000000000_0000_000004
+ |-eb_0000000000000_0000_000003
+ |-eb_0000000000000_0000_000002
+ |-eb_0000000000000_0000_000001
+-------------------------------------------------------------------------------
+Order of Execution
+-------------------------------------------------------------------------------
+1: eb_0000000000000_0000_000001
+2: eb_0000000000000_0000_000002
+3: eb_0000000000000_0000_000003
+4: eb_0000000000000_0000_000004
+5: eb_0000000000000_0000_000005
+-------------------------------------------------------------------------------
+
+=======================================================
+Block Id: eb_0000000000000_0000_000001 [LEAF]
+=======================================================
+
+[Outgoing]
+[q_0000000000000_0000] 1 => 3 (type=HASH_SHUFFLE, key=?upper_1 (TEXT), num=32)
+
+SCAN(0) on default.nation as n2
+ => target list: default.n2.n_name (TEXT), upper(default.n2.n_name (TEXT)) as ?upper_1
+ => out schema: {(2) default.n2.n_name (TEXT), ?upper_1 (TEXT)}
+ => in schema: {(4) default.n2.n_comment (TEXT), default.n2.n_name (TEXT), default.n2.n_nationkey (INT4), default.n2.n_regionkey (INT4)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000002 [LEAF]
+=======================================================
+
+[Outgoing]
+[q_0000000000000_0000] 2 => 3 (type=HASH_SHUFFLE, key=default.n1.n_name (TEXT), num=32)
+
+SCAN(1) on default.nation as n1
+ => target list: default.n1.n_nationkey (INT4), default.n1.n_name (TEXT)
+ => out schema: {(2) default.n1.n_nationkey (INT4), default.n1.n_name (TEXT)}
+ => in schema: {(4) default.n1.n_comment (TEXT), default.n1.n_name (TEXT), default.n1.n_nationkey (INT4), default.n1.n_regionkey (INT4)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000003 [INTERMEDIATE]
+=======================================================
+
+[Incoming]
+[q_0000000000000_0000] 1 => 3 (type=HASH_SHUFFLE, key=?upper_1 (TEXT), num=32)
+[q_0000000000000_0000] 2 => 3 (type=HASH_SHUFFLE, key=default.n1.n_name (TEXT), num=32)
+
+[Outgoing]
+[q_0000000000000_0000] 3 => 4 (type=RANGE_SHUFFLE, key=default.n1.n_nationkey (INT4), num=32)
+
+SORT(10)
+ => Sort Keys: default.n1.n_nationkey (INT4) (asc)
+ JOIN(6)(INNER)
+ => Join Cond: default.n1.n_name (TEXT) = ?upper_1 (TEXT)
+ => target list: default.n1.n_name (TEXT), default.n1.n_nationkey (INT4), default.n2.n_name (TEXT)
+ => out schema: {(3) default.n1.n_name (TEXT), default.n1.n_nationkey (INT4), default.n2.n_name (TEXT)}
+ => in schema: {(4) ?upper_1 (TEXT), default.n1.n_name (TEXT), default.n1.n_nationkey (INT4), default.n2.n_name (TEXT)}
+ SCAN(9) on eb_0000000000000_0000_000002
+ => out schema: {(2) default.n1.n_nationkey (INT4), default.n1.n_name (TEXT)}
+ => in schema: {(2) default.n1.n_nationkey (INT4), default.n1.n_name (TEXT)}
+ SCAN(8) on eb_0000000000000_0000_000001
+ => out schema: {(2) default.n2.n_name (TEXT), ?upper_1 (TEXT)}
+ => in schema: {(2) default.n2.n_name (TEXT), ?upper_1 (TEXT)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000004 [ROOT]
+=======================================================
+
+[Incoming]
+[q_0000000000000_0000] 3 => 4 (type=RANGE_SHUFFLE, key=default.n1.n_nationkey (INT4), num=32)
+
+[Enforcers]
+ 0: sorted input=eb_0000000000000_0000_000003
+
+SORT(3)
+ => Sort Keys: default.n1.n_nationkey (INT4) (asc)
+ SCAN(11) on eb_0000000000000_0000_000003
+ => out schema: {(3) default.n1.n_name (TEXT), default.n1.n_nationkey (INT4), default.n2.n_name (TEXT)}
+ => in schema: {(3) default.n1.n_name (TEXT), default.n1.n_nationkey (INT4), default.n2.n_name (TEXT)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000005 [TERMINAL]
+=======================================================
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.3.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.3.result b/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.3.result
new file mode 100644
index 0000000..c4e8c2c
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSelectQuery/testExplainSelectPhysical.3.result
@@ -0,0 +1,89 @@
+explain
+-------------------------------
+-------------------------------------------------------------------------------
+Execution Block Graph (TERMINAL - eb_0000000000000_0000_000004)
+-------------------------------------------------------------------------------
+|-eb_0000000000000_0000_000004
+ |-eb_0000000000000_0000_000003
+ |-eb_0000000000000_0000_000002
+ |-eb_0000000000000_0000_000001
+-------------------------------------------------------------------------------
+Order of Execution
+-------------------------------------------------------------------------------
+1: eb_0000000000000_0000_000001
+2: eb_0000000000000_0000_000002
+3: eb_0000000000000_0000_000003
+4: eb_0000000000000_0000_000004
+-------------------------------------------------------------------------------
+
+=======================================================
+Block Id: eb_0000000000000_0000_000001 [LEAF]
+=======================================================
+
+[Outgoing]
+[q_0000000000000_0000] 1 => 2 (type=HASH_SHUFFLE, key=?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), num=32)
+
+[Enforcers]
+ 0: type=Distinct,alg=hash
+
+DISTINCT_GROUP_BY(9)(l_linenumber)
+ => exprs: (count( distinct default.lineitem.l_orderkey (INT4)),sum( distinct default.lineitem.l_orderkey (INT4)),count())
+ => target list: ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)
+ => out schema:{(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+ => in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ => distinct: true, GROUP_BY(10)(l_orderkey), exprs: (count( distinct default.lineitem.l_orderkey (INT4)),sum( distinct default.lineitem.l_orderkey (INT4))), target list:{default.lineitem.l_orderkey (INT4), ?count_1 (INT8), ?sum_2 (INT8)}, out schema:{(3) default.lineitem.l_orderkey (INT4), ?count_1 (INT8), ?sum_2 (INT8)}, in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ => distinct: false, GROUP_BY(11)(), exprs: (count()), target list:{?count (INT8)}, out schema:{(1) ?count (INT8)}, in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ SCAN(0) on default.lineitem
+ => target list: default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)
+ => out schema: {(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ => in schema: {(16) default.lineitem.l_comment (TEXT), default.lineitem.l_commitdate (TEXT), default.lineitem.l_discount (FLOAT8), default.lineitem.l_extendedprice (FLOAT8), default.lineitem.l_linenumber (INT4), default.lineitem.l_linestatus (TEXT), default.lineitem.l_orderkey (INT4), default.lineitem.l_partkey (INT4), default.lineitem.l_quantity (FLOAT8), default.lineitem.l_receiptdate (TEXT), default.lineitem.l_returnflag (TEXT), default.lineitem.l_shipdate (TEXT), default.lineitem.l_shipinstruct (TEXT), default.lineitem.l_shipmode (TEXT), default.lineitem.l_suppkey (INT4), default.lineitem.l_tax (FLOAT8)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000002 [INTERMEDIATE]
+=======================================================
+
+[Incoming]
+[q_0000000000000_0000] 1 => 2 (type=HASH_SHUFFLE, key=?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), num=32)
+
+[Outgoing]
+[q_0000000000000_0000] 2 => 3 (type=HASH_SHUFFLE, key=default.lineitem.l_linenumber (INT4), num=32)
+
+[Enforcers]
+ 0: type=Distinct,alg=hash
+
+DISTINCT_GROUP_BY(12)(l_linenumber)
+ => exprs: (count( distinct default.lineitem.l_orderkey (INT4)),sum( distinct default.lineitem.l_orderkey (INT4)),count(?count (INT8)))
+ => target list: ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)
+ => out schema:{(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+ => in schema:{(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+ => distinct: true, GROUP_BY(13)(l_orderkey), exprs: (count( distinct default.lineitem.l_orderkey (INT4)),sum( distinct default.lineitem.l_orderkey (INT4))), target list:{default.lineitem.l_orderkey (INT4), ?count_1 (INT8), ?sum_2 (INT8)}, out schema:{(3) default.lineitem.l_orderkey (INT4), ?count_1 (INT8), ?sum_2 (INT8)}, in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ => distinct: false, GROUP_BY(14)(), exprs: (count(?count (INT8))), target list:{?count (INT8)}, out schema:{(1) ?count (INT8)}, in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ SCAN(18) on eb_0000000000000_0000_000001
+ => out schema: {(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+ => in schema: {(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000003 [ROOT]
+=======================================================
+
+[Incoming]
+[q_0000000000000_0000] 2 => 3 (type=HASH_SHUFFLE, key=default.lineitem.l_linenumber (INT4), num=32)
+
+[Enforcers]
+ 0: type=Distinct,alg=sort,keys=default.lineitem.l_orderkey |
+
+HAVING(2) (?sum_2 (INT8) = 6)
+ DISTINCT_GROUP_BY(15)(l_linenumber)
+ => exprs: (count( distinct default.lineitem.l_orderkey (INT4)),sum( distinct default.lineitem.l_orderkey (INT4)),count(?count (INT8)))
+ => target list: ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)
+ => out schema:{(4) default.lineitem.l_linenumber (INT4), ?count (INT8), ?count_1 (INT8), ?sum_2 (INT8)}
+ => in schema:{(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+ => distinct: true, GROUP_BY(16)(l_orderkey), exprs: (count( distinct default.lineitem.l_orderkey (INT4)),sum( distinct default.lineitem.l_orderkey (INT4))), target list:{default.lineitem.l_orderkey (INT4), ?count_1 (INT8), ?sum_2 (INT8)}, out schema:{(3) default.lineitem.l_orderkey (INT4), ?count_1 (INT8), ?sum_2 (INT8)}, in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ => distinct: false, GROUP_BY(17)(), exprs: (count(?count (INT8))), target list:{?count (INT8)}, out schema:{(1) ?count (INT8)}, in schema:{(2) default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4)}
+ SCAN(19) on eb_0000000000000_0000_000002
+ => out schema: {(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+ => in schema: {(4) ?distinctseq (INT2), default.lineitem.l_linenumber (INT4), default.lineitem.l_orderkey (INT4), ?count (INT8)}
+
+=======================================================
+Block Id: eb_0000000000000_0000_000004 [TERMINAL]
+=======================================================
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
index 0425f2e..17f79da 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
@@ -68,7 +68,12 @@ public class LogicalPlan {
/** planning and optimization log */
private List<String> planingHistory = Lists.newArrayList();
- private boolean isExplain;
+ private static enum ExplainType {
+ NOT_EXPLAIN,
+ EXPLAIN_LOGICAL,
+ EXPLAIN_GLOBAL
+ }
+ private ExplainType explainType = ExplainType.NOT_EXPLAIN;
public LogicalPlan(LogicalPlanner planner) {
}
@@ -104,12 +109,16 @@ public class LogicalPlan {
}
}
- public void setExplain() {
- isExplain = true;
+ public void setExplain(boolean isGlobal) {
+ explainType = isGlobal ? ExplainType.EXPLAIN_GLOBAL : ExplainType.EXPLAIN_LOGICAL;
}
public boolean isExplain() {
- return isExplain;
+ return explainType != ExplainType.NOT_EXPLAIN;
+ }
+
+ public boolean isExplainGlobal() {
+ return explainType == ExplainType.EXPLAIN_GLOBAL;
}
/**
http://git-wip-us.apache.org/repos/asf/tajo/blob/3e9a2dd2/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
index 8395c3d..ff3d6c2 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
@@ -200,7 +200,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
}
public LogicalNode visitExplain(PlanContext ctx, Stack<Expr> stack, Explain expr) throws PlanningException {
- ctx.plan.setExplain();
+ ctx.plan.setExplain(expr.isGlobal());
return visit(ctx, stack, expr.getChild());
}