diff --git a/sql/src/main/codegen/templates/Parser.jj b/sql/src/main/codegen/templates/Parser.jj index 49a820031cde..c76641755440 100644 --- a/sql/src/main/codegen/templates/Parser.jj +++ b/sql/src/main/codegen/templates/Parser.jj @@ -1173,7 +1173,7 @@ SqlExplain.Depth ExplainDepth() : } | { - return SqlExplain.Depth.PHYSICAL; + return SqlExplain.Depth.LOGICAL; } ) diff --git a/sql/src/main/java/io/druid/sql/calcite/planner/DruidPlanner.java b/sql/src/main/java/io/druid/sql/calcite/planner/DruidPlanner.java index d1ce4331b461..891972a92372 100644 --- a/sql/src/main/java/io/druid/sql/calcite/planner/DruidPlanner.java +++ b/sql/src/main/java/io/druid/sql/calcite/planner/DruidPlanner.java @@ -19,6 +19,7 @@ package io.druid.sql.calcite.planner; +import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -36,6 +37,7 @@ import io.druid.java.util.common.guava.Sequence; import io.druid.java.util.common.logger.Logger; import io.druid.query.Query; +import io.druid.query.QueryInterruptedException; import io.druid.query.QueryRunners; import io.druid.segment.incremental.IncrementalIndexSchema; import io.druid.sql.calcite.Utils; @@ -290,17 +292,27 @@ public T next() private PlannerResult handleExplain(final RelNode rel, final SqlExplain explain) { - final String explanation = RelOptUtil.dumpPlan("", rel, explain.getFormat(), explain.getDetailLevel()); - final Supplier> resultsSupplier = Suppliers.ofInstance( - Sequences.simple(ImmutableList.of(new Object[]{explanation}))); + final String explanation; + if (explain.withImplementation() && rel instanceof DruidRel) { + try { + plannerContext.disableQueryId(); + Query query = ((DruidRel) rel).toDruidQuery(true).getQuery(); + explanation = plannerContext.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(query); + } + catch (JsonProcessingException e) { + throw QueryInterruptedException.wrapIfNeeded(e); + } + } else { + explanation = RelOptUtil.dumpPlan("", rel, explain.getFormat(), explain.getDetailLevel()); + } final RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory(); - return new PlannerResult( - resultsSupplier, - typeFactory.createStructType( - ImmutableList.of(typeFactory.createSqlType(SqlTypeName.VARCHAR)), - ImmutableList.of("PLAN") - ) + final RelDataType resultType = typeFactory.createStructType( + ImmutableList.of(typeFactory.createSqlType(SqlTypeName.VARCHAR)), + ImmutableList.of("PLAN") ); + final Supplier> resultsSupplier = Suppliers.ofInstance( + Sequences.simple(ImmutableList.of(new Object[]{explanation}))); + return new PlannerResult(resultsSupplier, resultType); } @SuppressWarnings("unchecked") diff --git a/sql/src/main/java/io/druid/sql/calcite/planner/PlannerContext.java b/sql/src/main/java/io/druid/sql/calcite/planner/PlannerContext.java index a72952e5df6b..77cc4b6a7237 100644 --- a/sql/src/main/java/io/druid/sql/calcite/planner/PlannerContext.java +++ b/sql/src/main/java/io/druid/sql/calcite/planner/PlannerContext.java @@ -134,6 +134,12 @@ public String getQueryId() return (String) queryContext.get(Query.QUERYID); } + // for explain with implementation + public void disableQueryId() + { + queryContext.remove(Query.QUERYID); + } + public DruidOperatorTable getOperatorTable() { return operatorTable; diff --git a/sql/src/main/java/io/druid/sql/http/SqlResource.java b/sql/src/main/java/io/druid/sql/http/SqlResource.java index c4ad075cc65d..73d7fc9ac2b6 100644 --- a/sql/src/main/java/io/druid/sql/http/SqlResource.java +++ b/sql/src/main/java/io/druid/sql/http/SqlResource.java @@ -103,7 +103,8 @@ public Response explain( @Context HttpServletRequest req ) throws SQLException, IOException { - return execute(new SqlQuery(String.format("DESCRIBE %s", sqlQuery), null, false, contextFromParam(req)), req); + final String explain = String.format("EXPLAIN PLAN WITH IMPLEMENTATION FOR %s", sqlQuery); + return execute(new SqlQuery(explain, null, false, contextFromParam(req)), req); } @POST diff --git a/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java index 933905db966f..a6ba607f990c 100644 --- a/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java @@ -2056,7 +2056,7 @@ public void testCountStarOnView() throws Exception @Test public void testExplainCountStarOnView() throws Exception { - final String explanation = + String explanation = "DruidQueryRel(query=[" + "{\"queryType\":\"timeseries\"," + "\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"}," @@ -2071,6 +2071,57 @@ public void testExplainCountStarOnView() throws Exception new Object[]{explanation} ) ); + + explanation = "{\n" + + " \"queryType\" : \"timeseries\",\n" + + " \"dataSource\" : {\n" + + " \"type\" : \"table\",\n" + + " \"name\" : \"foo\"\n" + + " },\n" + + " \"descending\" : false,\n" + + " \"filter\" : {\n" + + " \"type\" : \"and\",\n" + + " \"fields\" : [ {\n" + + " \"type\" : \"selector\",\n" + + " \"dimension\" : \"dim2\",\n" + + " \"value\" : \"a\"\n" + + " }, {\n" + + " \"type\" : \"not\",\n" + + " \"field\" : {\n" + + " \"type\" : \"selector\",\n" + + " \"dimension\" : \"dim1\",\n" + + " \"value\" : \"z\",\n" + + " \"extractionFn\" : {\n" + + " \"type\" : \"substring\",\n" + + " \"index\" : 0,\n" + + " \"length\" : 1\n" + + " }\n" + + " }\n" + + " } ]\n" + + " },\n" + + " \"granularity\" : {\n" + + " \"type\" : \"all\"\n" + + " },\n" + + " \"aggregations\" : [ {\n" + + " \"type\" : \"count\",\n" + + " \"name\" : \"a0\"\n" + + " } ],\n" + + " \"limitSpec\" : {\n" + + " \"type\" : \"noop\"\n" + + " },\n" + + " \"outputColumns\" : [ \"a0\" ],\n" + + " \"context\" : {\n" + + " \"sqlCurrentTimestamp\" : \"2000-01-01T00:00:00Z\",\n" + + " \"groupby.sort.on.time\" : false\n" + + " }\n" + + "}"; + testQuery( + "EXPLAIN PLAN WITH IMPLEMENTATION FOR SELECT COUNT(*) FROM aview WHERE dim1_firstchar <> 'z'", + ImmutableList.of(), + ImmutableList.of( + new Object[]{explanation} + ) + ); } // @Test