Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include statement attributes in EXPLAIN PLAN output #14074

Merged
merged 17 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ public void validate() throws SqlParseException, ValidationException
try {
handler.validate();
plannerContext.setResourceActions(handler.resourceActions());
plannerContext.setStatementKind(handler.statementKind());
plannerContext.setTargetDataSource(handler.targetDataSource());
}
catch (RuntimeException e) {
throw new ValidationException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.apache.druid.sql.calcite.run.EngineFeature;
import org.apache.druid.sql.calcite.run.QueryMaker;

import javax.annotation.Nullable;
import java.util.List;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -273,6 +274,19 @@ public void validate() throws ValidationException
}
super.validate();
}

@Override
public String statementKind()
{
return DruidSqlInsert.OPERATOR.getName();
}

@Nullable
abhishekrb19 marked this conversation as resolved.
Show resolved Hide resolved
@Override
public SqlNode targetDataSource()
{
return sqlNode.getTargetTable();
}
}

/**
Expand Down Expand Up @@ -331,5 +345,18 @@ public void validate() throws ValidationException
);
}
}

@Override
public String statementKind()
{
return DruidSqlReplace.OPERATOR.getName();
}

@Nullable
abhishekrb19 marked this conversation as resolved.
Show resolved Hide resolved
@Override
public SqlNode targetDataSource()
{
return sqlNode.getTargetTable();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.calcite.avatica.remote.TypedValue;
import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlNode;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.ISE;
import org.apache.druid.java.util.common.Numbers;
Expand Down Expand Up @@ -111,6 +112,10 @@ public class PlannerContext
private String planningError;
private QueryMaker queryMaker;
private VirtualColumnRegistry joinExpressionVirtualColumnRegistry;
private String statementKind;
// Only valid for INSERT/REPLACE queries
@Nullable
private SqlNode targetDataSource;

private PlannerContext(
final PlannerToolbox plannerToolbox,
Expand Down Expand Up @@ -502,4 +507,31 @@ public void setJoinExpressionVirtualColumnRegistry(VirtualColumnRegistry joinExp
{
this.joinExpressionVirtualColumnRegistry = joinExpressionVirtualColumnRegistry;
}

public String getStatementKind()
{
return this.statementKind;
}

public void setStatementKind(String statementKind)
{
if (this.statementKind != null) {
throw new ISE("StatementKind has already been set");
}
this.statementKind = statementKind;
}

@Nullable
public SqlNode getTargetDataSource()
abhishekrb19 marked this conversation as resolved.
Show resolved Hide resolved
{
return this.targetDataSource;
}

public void setTargetDataSource(SqlNode targetDataSource)
{
if (this.targetDataSource != null) {
throw new ISE("TargetDataSource has already been set");
}
this.targetDataSource = targetDataSource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ public PlannerResult plan() throws ValidationException
throw new UnsupportedSQLQueryException(errorMessage);
}
}
@Override
public String statementKind()
{
return "SELECT";
}

/**
* No target datasource for SELECT/non-DML statements.
*/
@Nullable
@Override
public SqlNode targetDataSource()
{
// nothing for SELECT queries.
return null;
}

private static Set<RelOptTable> getBindableTables(final RelNode relNode)
{
Expand Down Expand Up @@ -384,7 +400,7 @@ protected PlannerResult planExplanation(

/**
* This method doesn't utilize the Calcite's internal {@link RelOptUtil#dumpPlan} since that tends to be verbose
* and not indicative of the native Druid Queries which will get executed
* and not indicative of the native Druid Queries which will get executed.
* This method assumes that the Planner has converted the RelNodes to DruidRels, and thereby we can implicitly cast it
*
* @param rel Instance of the root {@link DruidRel} which is formed by running the planner transformations on it
Expand Down Expand Up @@ -414,6 +430,10 @@ private String explainSqlPlanAsNativeQueries(DruidRel<?> rel) throws JsonProcess
nativeQueriesArrayNode.add(objectNode);
}

nativeQueriesArrayNode.add(jsonMapper.createObjectNode().put(
"statementKind", jsonMapper.convertValue(rel.getPlannerContext().getStatementKind(), String.class)));
nativeQueriesArrayNode.add(jsonMapper.createObjectNode().put(
"targetDataSource", StringUtils.format("%s", rel.getPlannerContext().getTargetDataSource())));
return jsonMapper.writeValueAsString(nativeQueriesArrayNode);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.tools.ValidationException;
import org.apache.druid.query.QueryContext;
import org.apache.druid.server.security.ResourceAction;
import org.apache.druid.sql.calcite.run.SqlEngine;
import org.joda.time.DateTimeZone;

import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;

Expand All @@ -42,6 +44,18 @@ public interface SqlStatementHandler
PrepareResult prepareResult();
PlannerResult plan() throws ValidationException;

/**
* @return the statement kind for a SQL statement. For example, SELECT, INSERT, or REPLACE.
*/
String statementKind();

/**
*
* @return the SQL node representing the target table in a SQL statement. Returns
* null for SELECT/non-DML statements.
*/
@Nullable SqlNode targetDataSource();
abhishekrb19 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Context available to statement handlers.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public void testExplainSelectCount() throws SQLException
ImmutableMap.of(
"PLAN",
StringUtils.format(
"[{\"query\":{\"queryType\":\"timeseries\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"granularity\":{\"type\":\"all\"},\"aggregations\":[{\"type\":\"count\",\"name\":\"a0\"}],\"context\":{\"sqlQueryId\":\"%s\",\"sqlStringifyArrays\":false,\"sqlTimeZone\":\"America/Los_Angeles\"}},\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]}]",
"[{\"query\":{\"queryType\":\"timeseries\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"granularity\":{\"type\":\"all\"},\"aggregations\":[{\"type\":\"count\",\"name\":\"a0\"}],\"context\":{\"sqlQueryId\":\"%s\",\"sqlStringifyArrays\":false,\"sqlTimeZone\":\"America/Los_Angeles\"}},\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]},{\"statementKind\":\"SELECT\"},{\"targetDataSource\":\"null\"}]",
DUMMY_SQL_QUERY_ID
),
"RESOURCES",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ public static void setupNullValues()
public static final PlannerConfig PLANNER_CONFIG_AUTHORIZE_SYS_TABLES =
PlannerConfig.builder().authorizeSystemTablesDirectly(true).build();

public static final PlannerConfig PLANNER_CONFIG_LEGACY_QUERY_EXPLAIN =
PlannerConfig.builder().useNativeQueryExplain(false).build();

public static final PlannerConfig PLANNER_CONFIG_NATIVE_QUERY_EXPLAIN =
PlannerConfig.builder().useNativeQueryExplain(true).build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ public void testExplainCountStarOnView()
+ "\"granularity\":{\"type\":\"all\"},"
+ "\"aggregations\":[{\"type\":\"count\",\"name\":\"a0\"}],"
+ "\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"}},"
+ "\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]"
+ "}]";
+ "\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";
final String resources = "[{\"name\":\"aview\",\"type\":\"VIEW\"}]";

testQuery(
PlannerConfig.builder().useNativeQueryExplain(false).build(),
PLANNER_CONFIG_LEGACY_QUERY_EXPLAIN,
query,
CalciteTests.REGULAR_USER_AUTH_RESULT,
ImmutableList.of(),
Expand Down Expand Up @@ -122,8 +124,10 @@ public void testExplainExactCountDistinctOfSemiJoinResult()
+ "\"aggregations\":[{\"type\":\"count\",\"name\":\"a0\"}],"
+ "\"limitSpec\":{\"type\":\"NoopLimitSpec\"},"
+ "\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"}},"
+ "\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]"
+ "}]";
+ "\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";
final String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";

testQuery(
Expand All @@ -133,15 +137,15 @@ public void testExplainExactCountDistinctOfSemiJoinResult()
);

testQuery(
PlannerConfig.builder().useNativeQueryExplain(false).build(),
PLANNER_CONFIG_LEGACY_QUERY_EXPLAIN,
query,
CalciteTests.REGULAR_USER_AUTH_RESULT,
ImmutableList.of(),
ImmutableList.of(new Object[]{legacyExplanation, resources})
);
}

// This testcase has been added here and not in CalciteSelectQueryTests since this checks if the overrides are working
// This testcase has been added here and not in CalciteSelectQueryTest since this checks if the overrides are working
// properly when displaying the output of "EXPLAIN PLAN FOR ..." queries
@Test
public void testExplainSelectStarWithOverrides()
Expand All @@ -165,8 +169,10 @@ public void testExplainSelectStarWithOverrides()
+ "\"legacy\":false,"
+ "\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},"
+ "\"granularity\":{\"type\":\"all\"}},"
+ "\"signature\":[{\"name\":\"__time\",\"type\":\"LONG\"},{\"name\":\"dim1\",\"type\":\"STRING\"},{\"name\":\"dim2\",\"type\":\"STRING\"},{\"name\":\"dim3\",\"type\":\"STRING\"},{\"name\":\"cnt\",\"type\":\"LONG\"},{\"name\":\"m1\",\"type\":\"FLOAT\"},{\"name\":\"m2\",\"type\":\"DOUBLE\"},{\"name\":\"unique_dim1\",\"type\":\"COMPLEX<hyperUnique>\"}]"
+ "}]";
+ "\"signature\":[{\"name\":\"__time\",\"type\":\"LONG\"},{\"name\":\"dim1\",\"type\":\"STRING\"},{\"name\":\"dim2\",\"type\":\"STRING\"},{\"name\":\"dim3\",\"type\":\"STRING\"},{\"name\":\"cnt\",\"type\":\"LONG\"},{\"name\":\"m1\",\"type\":\"FLOAT\"},{\"name\":\"m2\",\"type\":\"DOUBLE\"},{\"name\":\"unique_dim1\",\"type\":\"COMPLEX<hyperUnique>\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";

String explanationWithContext = "[{"
+ "\"query\":{\"queryType\":\"scan\","
Expand All @@ -177,8 +183,10 @@ public void testExplainSelectStarWithOverrides()
+ "\"legacy\":false,"
+ "\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"useNativeQueryExplain\":true,\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},"
+ "\"granularity\":{\"type\":\"all\"}},"
+ "\"signature\":[{\"name\":\"__time\",\"type\":\"LONG\"},{\"name\":\"dim1\",\"type\":\"STRING\"},{\"name\":\"dim2\",\"type\":\"STRING\"},{\"name\":\"dim3\",\"type\":\"STRING\"},{\"name\":\"cnt\",\"type\":\"LONG\"},{\"name\":\"m1\",\"type\":\"FLOAT\"},{\"name\":\"m2\",\"type\":\"DOUBLE\"},{\"name\":\"unique_dim1\",\"type\":\"COMPLEX<hyperUnique>\"}]"
+ "}]";
+ "\"signature\":[{\"name\":\"__time\",\"type\":\"LONG\"},{\"name\":\"dim1\",\"type\":\"STRING\"},{\"name\":\"dim2\",\"type\":\"STRING\"},{\"name\":\"dim3\",\"type\":\"STRING\"},{\"name\":\"cnt\",\"type\":\"LONG\"},{\"name\":\"m1\",\"type\":\"FLOAT\"},{\"name\":\"m2\",\"type\":\"DOUBLE\"},{\"name\":\"unique_dim1\",\"type\":\"COMPLEX<hyperUnique>\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";
String sql = "EXPLAIN PLAN FOR SELECT * FROM druid.foo";
String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";

Expand Down Expand Up @@ -239,11 +247,17 @@ public void testExplainMultipleTopLevelUnionAllQueries()
+ "{"
+ "\"query\":{\"queryType\":\"scan\",\"dataSource\":{\"type\":\"table\",\"name\":\"foo\"},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"resultFormat\":\"compactedList\",\"filter\":{\"type\":\"selector\",\"dimension\":\"dim1\",\"value\":\"44\"},\"columns\":[\"dim1\"],\"legacy\":false,\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},\"granularity\":{\"type\":\"all\"}},"
+ "\"signature\":[{\"name\":\"dim1\",\"type\":\"STRING\"}]"
+ "},"
+ "{"
+ "\"statementKind\":\"SELECT\""
+ "},"
+ "{"
+ "\"targetDataSource\":\"null\""
+ "}]";
final String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";

testQuery(
PlannerConfig.builder().useNativeQueryExplain(false).build(),
PLANNER_CONFIG_LEGACY_QUERY_EXPLAIN,
query,
CalciteTests.REGULAR_USER_AUTH_RESULT,
ImmutableList.of(),
Expand Down Expand Up @@ -291,8 +305,10 @@ public void testExplainSelectMvfilterExpressions()
+ "\"legacy\":false,"
+ "\"context\":{\"defaultTimeout\":300000,\"forceExpressionVirtualColumns\":true,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"useNativeQueryExplain\":true,\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},"
+ "\"granularity\":{\"type\":\"all\"}},"
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"STRING\"},{\"name\":\"v1\",\"type\":\"STRING\"}]"
+ "}]";
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"STRING\"},{\"name\":\"v1\",\"type\":\"STRING\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";
final String expectedResources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";

testQuery(
Expand All @@ -316,9 +332,10 @@ public void testExplainSelectMvfilterExpressions()
+ "\"legacy\":false,"
+ "\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"useNativeQueryExplain\":true,\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},"
+ "\"granularity\":{\"type\":\"all\"}},"
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"STRING\"},{\"name\":\"v1\",\"type\":\"STRING\"}]"
+ "}]";

+ "\"signature\":[{\"name\":\"v0\",\"type\":\"STRING\"},{\"name\":\"v1\",\"type\":\"STRING\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";
final Map<String, Object> mvFilteredContext = new HashMap<>(QUERY_CONTEXT_DEFAULT);
mvFilteredContext.put(PlannerConfig.CTX_KEY_USE_NATIVE_QUERY_EXPLAIN, true);

Expand Down Expand Up @@ -355,8 +372,10 @@ public void testExplainSelectTimestampExpression()
+ "\"legacy\":false,"
+ "\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"useNativeQueryExplain\":true,\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},"
+ "\"granularity\":{\"type\":\"all\"}},"
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"LONG\"}]"
+ "}]";
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"LONG\"}]},"
+ "{\"statementKind\":\"SELECT\"},"
+ "{\"targetDataSource\":\"null\"}"
+ "]";
final String expectedResources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";

// Verify the query plan
Expand Down
Loading