mirror of https://github.com/apache/druid.git
Include statement attributes in `EXPLAIN PLAN` output (#14074)
This commit adds attributes that contain metadata information about the query in the EXPLAIN PLAN output. The attributes currently contain two items: - `statementTyp`: SELECT, INSERT or REPLACE - `targetDataSource`: provides the target datasource name for DML statements It is added to both the legacy and native query plan outputs.
This commit is contained in:
parent
be6745f75b
commit
c98c66558f
|
@ -64,7 +64,11 @@ appreciated.
|
|||
|
||||
The [EXPLAIN PLAN](sql.md#explain-plan) functionality can help you understand how a given SQL query will
|
||||
be translated to native.
|
||||
EXPLAIN PLAN statements return a `RESOURCES` column that describes the resource being queried as well as a `PLAN` column that contains a JSON array of native queries that Druid will run.
|
||||
EXPLAIN PLAN statements return:
|
||||
- a `PLAN` column that contains a JSON array of native queries that Druid will run
|
||||
- a `RESOURCES` column that describes the resource being queried as well as a `PLAN` column that contains a JSON array of native queries that Druid will run
|
||||
- a `ATTRIBUTES` column that describes the attributes of a query, such as the statement type and target data source
|
||||
|
||||
For example, consider the following query:
|
||||
|
||||
```sql
|
||||
|
@ -77,120 +81,132 @@ WHERE channel IN (SELECT page FROM wikipedia GROUP BY page ORDER BY COUNT(*) DES
|
|||
GROUP BY channel
|
||||
```
|
||||
|
||||
The EXPLAIN PLAN statement returns the following plan:
|
||||
The EXPLAIN PLAN statement returns the following result with plan, resources, and attributes information in it:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"query": {
|
||||
"queryType": "topN",
|
||||
"dataSource": {
|
||||
"type": "join",
|
||||
"left": {
|
||||
"type": "table",
|
||||
"name": "wikipedia"
|
||||
},
|
||||
"right": {
|
||||
"type": "query",
|
||||
"query": {
|
||||
"queryType": "groupBy",
|
||||
"dataSource": {
|
||||
"type": "table",
|
||||
"name": "wikipedia"
|
||||
},
|
||||
"intervals": {
|
||||
"type": "intervals",
|
||||
"intervals": [
|
||||
"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z"
|
||||
]
|
||||
},
|
||||
"granularity": {
|
||||
"type": "all"
|
||||
},
|
||||
"dimensions": [
|
||||
{
|
||||
"type": "default",
|
||||
"dimension": "page",
|
||||
"outputName": "d0",
|
||||
"outputType": "STRING"
|
||||
}
|
||||
],
|
||||
"aggregations": [
|
||||
{
|
||||
"type": "count",
|
||||
"name": "a0"
|
||||
}
|
||||
],
|
||||
"limitSpec": {
|
||||
"type": "default",
|
||||
"columns": [
|
||||
[
|
||||
{
|
||||
"query": {
|
||||
"queryType": "topN",
|
||||
"dataSource": {
|
||||
"type": "join",
|
||||
"left": {
|
||||
"type": "table",
|
||||
"name": "wikipedia"
|
||||
},
|
||||
"right": {
|
||||
"type": "query",
|
||||
"query": {
|
||||
"queryType": "groupBy",
|
||||
"dataSource": {
|
||||
"type": "table",
|
||||
"name": "wikipedia"
|
||||
},
|
||||
"intervals": {
|
||||
"type": "intervals",
|
||||
"intervals": [
|
||||
"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z"
|
||||
]
|
||||
},
|
||||
"granularity": {
|
||||
"type": "all"
|
||||
},
|
||||
"dimensions": [
|
||||
{
|
||||
"dimension": "a0",
|
||||
"direction": "descending",
|
||||
"dimensionOrder": {
|
||||
"type": "numeric"
|
||||
}
|
||||
"type": "default",
|
||||
"dimension": "page",
|
||||
"outputName": "d0",
|
||||
"outputType": "STRING"
|
||||
}
|
||||
],
|
||||
"limit": 10
|
||||
},
|
||||
"context": {
|
||||
"sqlOuterLimit": 101,
|
||||
"sqlQueryId": "ee616a36-c30c-4eae-af00-245127956e42",
|
||||
"useApproximateCountDistinct": false,
|
||||
"useApproximateTopN": false
|
||||
"aggregations": [
|
||||
{
|
||||
"type": "count",
|
||||
"name": "a0"
|
||||
}
|
||||
],
|
||||
"limitSpec": {
|
||||
"type": "default",
|
||||
"columns": [
|
||||
{
|
||||
"dimension": "a0",
|
||||
"direction": "descending",
|
||||
"dimensionOrder": {
|
||||
"type": "numeric"
|
||||
}
|
||||
}
|
||||
],
|
||||
"limit": 10
|
||||
},
|
||||
"context": {
|
||||
"sqlOuterLimit": 101,
|
||||
"sqlQueryId": "ee616a36-c30c-4eae-af00-245127956e42",
|
||||
"useApproximateCountDistinct": false,
|
||||
"useApproximateTopN": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"rightPrefix": "j0.",
|
||||
"condition": "(\"channel\" == \"j0.d0\")",
|
||||
"joinType": "INNER"
|
||||
},
|
||||
"dimension": {
|
||||
"type": "default",
|
||||
"dimension": "channel",
|
||||
"outputName": "d0",
|
||||
"outputType": "STRING"
|
||||
},
|
||||
"metric": {
|
||||
"type": "dimension",
|
||||
"ordering": {
|
||||
"type": "lexicographic"
|
||||
}
|
||||
},
|
||||
"rightPrefix": "j0.",
|
||||
"condition": "(\"channel\" == \"j0.d0\")",
|
||||
"joinType": "INNER"
|
||||
},
|
||||
"dimension": {
|
||||
"type": "default",
|
||||
"dimension": "channel",
|
||||
"outputName": "d0",
|
||||
"outputType": "STRING"
|
||||
},
|
||||
"metric": {
|
||||
"type": "dimension",
|
||||
"ordering": {
|
||||
"type": "lexicographic"
|
||||
"threshold": 101,
|
||||
"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": {
|
||||
"sqlOuterLimit": 101,
|
||||
"sqlQueryId": "ee616a36-c30c-4eae-af00-245127956e42",
|
||||
"useApproximateCountDistinct": false,
|
||||
"useApproximateTopN": false
|
||||
}
|
||||
},
|
||||
"threshold": 101,
|
||||
"intervals": {
|
||||
"type": "intervals",
|
||||
"intervals": [
|
||||
"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z"
|
||||
]
|
||||
},
|
||||
"granularity": {
|
||||
"type": "all"
|
||||
},
|
||||
"aggregations": [
|
||||
"signature": [
|
||||
{
|
||||
"type": "count",
|
||||
"name": "a0"
|
||||
"name": "d0",
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"name": "a0",
|
||||
"type": "LONG"
|
||||
}
|
||||
],
|
||||
"context": {
|
||||
"sqlOuterLimit": 101,
|
||||
"sqlQueryId": "ee616a36-c30c-4eae-af00-245127956e42",
|
||||
"useApproximateCountDistinct": false,
|
||||
"useApproximateTopN": false
|
||||
}
|
||||
},
|
||||
"signature": [
|
||||
{
|
||||
"name": "d0",
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"name": "a0",
|
||||
"type": "LONG"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "wikipedia",
|
||||
"type": "DATASOURCE"
|
||||
}
|
||||
],
|
||||
{
|
||||
"statementType": "SELECT",
|
||||
"targetDataSource": null
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
|
@ -250,8 +250,8 @@ Add "EXPLAIN PLAN FOR" to the beginning of any query to get information about ho
|
|||
the query will not actually be executed. Refer to the [Query translation](sql-translation.md#interpreting-explain-plan-output)
|
||||
documentation for more information on the output of EXPLAIN PLAN.
|
||||
|
||||
> Be careful when interpreting EXPLAIN PLAN output, and use [request logging](../configuration/index.md#request-logging) if in doubt.
|
||||
Request logs show the exact native query that will be run.
|
||||
> For the legacy plan, be careful when interpreting EXPLAIN PLAN output, and use [request logging](../configuration/index.md#request-logging) if in doubt.
|
||||
Request logs show the exact native query that will be run. Alternatively, to see the native query plan, set `useNativeQueryExplain` to true in the query context.
|
||||
|
||||
## Identifiers and literals
|
||||
|
||||
|
|
|
@ -135,6 +135,7 @@ public class DruidPlanner implements Closeable
|
|||
try {
|
||||
handler.validate();
|
||||
plannerContext.setResourceActions(handler.resourceActions());
|
||||
plannerContext.setExplainAttributes(handler.explainAttributes());
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw new ValidationException(e);
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.druid.sql.calcite.planner;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.calcite.sql.SqlNode;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* ExplainAttributes holds the attributes of a SQL statement that is used in the EXPLAIN PLAN result.
|
||||
*/
|
||||
public final class ExplainAttributes
|
||||
{
|
||||
private final String statementType;
|
||||
|
||||
@Nullable
|
||||
private final SqlNode targetDataSource;
|
||||
|
||||
public ExplainAttributes(
|
||||
@JsonProperty("statementType") final String statementType,
|
||||
@JsonProperty("targetDataSource") @Nullable final SqlNode targetDataSource)
|
||||
{
|
||||
this.statementType = statementType;
|
||||
this.targetDataSource = targetDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the statement kind of a SQL statement. For example, SELECT, INSERT, or REPLACE.
|
||||
*/
|
||||
@JsonProperty
|
||||
public String getStatementType()
|
||||
{
|
||||
return statementType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the target datasource in a SQL statement. Returns null
|
||||
* for SELECT/non-DML statements where there is no target datasource.
|
||||
*/
|
||||
@Nullable
|
||||
@JsonProperty
|
||||
public String getTargetDataSource()
|
||||
{
|
||||
return targetDataSource == null ? null : targetDataSource.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ExplainAttributes{" +
|
||||
"statementType='" + statementType + '\'' +
|
||||
", targetDataSource=" + targetDataSource +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -273,6 +273,15 @@ public abstract class IngestHandler extends QueryHandler
|
|||
}
|
||||
super.validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplainAttributes explainAttributes()
|
||||
{
|
||||
return new ExplainAttributes(
|
||||
DruidSqlInsert.OPERATOR.getName(),
|
||||
sqlNode.getTargetTable()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,5 +340,14 @@ public abstract class IngestHandler extends QueryHandler
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplainAttributes explainAttributes()
|
||||
{
|
||||
return new ExplainAttributes(
|
||||
DruidSqlReplace.OPERATOR.getName(),
|
||||
sqlNode.getTargetTable()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,8 @@ public class PlannerContext
|
|||
private String planningError;
|
||||
private QueryMaker queryMaker;
|
||||
private VirtualColumnRegistry joinExpressionVirtualColumnRegistry;
|
||||
// set of attributes for a SQL statement used in the EXPLAIN PLAN output
|
||||
private ExplainAttributes explainAttributes;
|
||||
|
||||
private PlannerContext(
|
||||
final PlannerToolbox plannerToolbox,
|
||||
|
@ -502,4 +504,18 @@ public class PlannerContext
|
|||
{
|
||||
this.joinExpressionVirtualColumnRegistry = joinExpressionVirtualColumnRegistry;
|
||||
}
|
||||
|
||||
public ExplainAttributes getExplainAttributes()
|
||||
{
|
||||
return this.explainAttributes;
|
||||
}
|
||||
|
||||
public void setExplainAttributes(ExplainAttributes explainAttributes)
|
||||
{
|
||||
if (this.explainAttributes != null) {
|
||||
throw new ISE("ExplainAttributes has already been set");
|
||||
}
|
||||
this.explainAttributes = explainAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -173,10 +173,11 @@ public abstract class QueryHandler extends SqlStatementHandler.BaseStatementHand
|
|||
{
|
||||
return typeFactory.createStructType(
|
||||
ImmutableList.of(
|
||||
Calcites.createSqlType(typeFactory, SqlTypeName.VARCHAR),
|
||||
Calcites.createSqlType(typeFactory, SqlTypeName.VARCHAR),
|
||||
Calcites.createSqlType(typeFactory, SqlTypeName.VARCHAR)
|
||||
),
|
||||
ImmutableList.of("PLAN", "RESOURCES")
|
||||
ImmutableList.of("PLAN", "RESOURCES", "ATTRIBUTES")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -229,6 +230,15 @@ public abstract class QueryHandler extends SqlStatementHandler.BaseStatementHand
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplainAttributes explainAttributes()
|
||||
{
|
||||
return new ExplainAttributes(
|
||||
"SELECT",
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
private static Set<RelOptTable> getBindableTables(final RelNode relNode)
|
||||
{
|
||||
class HasBindableVisitor extends RelVisitor
|
||||
|
@ -374,9 +384,19 @@ public abstract class QueryHandler extends SqlStatementHandler.BaseStatementHand
|
|||
log.error(jpe, "Encountered exception while serializing resources for explain output");
|
||||
resourcesString = null;
|
||||
}
|
||||
|
||||
String explainAttributesString;
|
||||
try {
|
||||
explainAttributesString = plannerContext.getJsonMapper().writeValueAsString(plannerContext.getExplainAttributes());
|
||||
}
|
||||
catch (JsonProcessingException jpe) {
|
||||
log.error(jpe, "Encountered exception while serializing attributes for explain output");
|
||||
explainAttributesString = null;
|
||||
}
|
||||
|
||||
final Supplier<QueryResponse<Object[]>> resultsSupplier = Suppliers.ofInstance(
|
||||
QueryResponse.withEmptyContext(
|
||||
Sequences.simple(ImmutableList.of(new Object[]{explanation, resourcesString}))
|
||||
Sequences.simple(ImmutableList.of(new Object[]{explanation, resourcesString, explainAttributesString}))
|
||||
)
|
||||
);
|
||||
return new PlannerResult(resultsSupplier, getExplainStructType(rel.getCluster().getTypeFactory()));
|
||||
|
@ -384,7 +404,7 @@ public abstract class QueryHandler extends SqlStatementHandler.BaseStatementHand
|
|||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -41,6 +41,7 @@ public interface SqlStatementHandler
|
|||
void prepare();
|
||||
PrepareResult prepareResult();
|
||||
PlannerResult plan() throws ValidationException;
|
||||
ExplainAttributes explainAttributes();
|
||||
|
||||
/**
|
||||
* Context available to statement handlers.
|
||||
|
|
|
@ -469,7 +469,9 @@ public class DruidAvaticaHandlerTest extends CalciteTestBase
|
|||
DUMMY_SQL_QUERY_ID
|
||||
),
|
||||
"RESOURCES",
|
||||
"[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]"
|
||||
"[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]",
|
||||
"ATTRIBUTES",
|
||||
"{\"statementType\":\"SELECT\",\"targetDataSource\":null}"
|
||||
)
|
||||
),
|
||||
getRows(resultSet)
|
||||
|
|
|
@ -170,6 +170,9 @@ public class BaseCalciteQueryTest extends CalciteTestBase
|
|||
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();
|
||||
|
||||
|
|
|
@ -51,14 +51,15 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]"
|
||||
+ "}]";
|
||||
final String resources = "[{\"name\":\"aview\",\"type\":\"VIEW\"}]";
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
|
||||
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}
|
||||
new Object[]{legacyExplanation, resources, attributes}
|
||||
)
|
||||
);
|
||||
testQuery(
|
||||
|
@ -67,7 +68,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
CalciteTests.REGULAR_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(
|
||||
new Object[]{explanation, resources}
|
||||
new Object[]{explanation, resources, attributes}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -81,6 +82,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ " BindableTableScan(table=[[INFORMATION_SCHEMA, COLUMNS]])\n";
|
||||
|
||||
final String resources = "[]";
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
|
||||
testQuery(
|
||||
"EXPLAIN PLAN FOR\n"
|
||||
|
@ -89,7 +91,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "WHERE TABLE_SCHEMA = 'druid' AND TABLE_NAME = 'foo'",
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(
|
||||
new Object[]{explanation, resources}
|
||||
new Object[]{explanation, resources, attributes}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -125,23 +127,24 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"signature\":[{\"name\":\"a0\",\"type\":\"LONG\"}]"
|
||||
+ "}]";
|
||||
final String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
|
||||
testQuery(
|
||||
query,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{explanation, resources})
|
||||
ImmutableList.of(new Object[]{explanation, resources, attributes})
|
||||
);
|
||||
|
||||
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})
|
||||
ImmutableList.of(new Object[]{legacyExplanation, resources, attributes})
|
||||
);
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
@ -181,16 +184,17 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "}]";
|
||||
String sql = "EXPLAIN PLAN FOR SELECT * FROM druid.foo";
|
||||
String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
|
||||
// Test when default config and no overrides
|
||||
testQuery(sql, ImmutableList.of(), ImmutableList.of(new Object[]{explanation, resources}));
|
||||
testQuery(sql, ImmutableList.of(), ImmutableList.of(new Object[]{explanation, resources, attributes}));
|
||||
|
||||
// Test when default config and useNativeQueryExplain is overridden in the context
|
||||
testQuery(
|
||||
sql,
|
||||
legacyExplainContext,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{legacyExplanationWithContext, resources})
|
||||
ImmutableList.of(new Object[]{legacyExplanationWithContext, resources, attributes})
|
||||
);
|
||||
|
||||
// Test when useNativeQueryExplain enabled by default and no overrides
|
||||
|
@ -199,7 +203,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
sql,
|
||||
CalciteTests.REGULAR_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{explanation, resources})
|
||||
ImmutableList.of(new Object[]{explanation, resources, attributes})
|
||||
);
|
||||
|
||||
// Test when useNativeQueryExplain enabled by default but is overriden in the context
|
||||
|
@ -209,7 +213,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
sql,
|
||||
CalciteTests.REGULAR_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{explanationWithContext, resources})
|
||||
ImmutableList.of(new Object[]{explanationWithContext, resources, attributes})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -241,14 +245,14 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"signature\":[{\"name\":\"dim1\",\"type\":\"STRING\"}]"
|
||||
+ "}]";
|
||||
final String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";
|
||||
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
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}
|
||||
new Object[]{legacyExplanation, resources, attributes}
|
||||
)
|
||||
);
|
||||
testQuery(
|
||||
|
@ -257,7 +261,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
CalciteTests.REGULAR_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(
|
||||
new Object[]{explanation, resources}
|
||||
new Object[]{explanation, resources, attributes}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -294,12 +298,12 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"STRING\"},{\"name\":\"v1\",\"type\":\"STRING\"}]"
|
||||
+ "}]";
|
||||
final String expectedResources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";
|
||||
|
||||
final String expectedAttributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
testQuery(
|
||||
explainSql,
|
||||
defaultExprContext,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{expectedPlanWithDefaultExpressions, expectedResources})
|
||||
ImmutableList.of(new Object[]{expectedPlanWithDefaultExpressions, expectedResources, expectedAttributes})
|
||||
);
|
||||
|
||||
// Test plan as mv-filtered virtual columns
|
||||
|
@ -318,7 +322,6 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"granularity\":{\"type\":\"all\"}},"
|
||||
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"STRING\"},{\"name\":\"v1\",\"type\":\"STRING\"}]"
|
||||
+ "}]";
|
||||
|
||||
final Map<String, Object> mvFilteredContext = new HashMap<>(QUERY_CONTEXT_DEFAULT);
|
||||
mvFilteredContext.put(PlannerConfig.CTX_KEY_USE_NATIVE_QUERY_EXPLAIN, true);
|
||||
|
||||
|
@ -326,7 +329,7 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
explainSql,
|
||||
mvFilteredContext,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{expectedPlanWithMvfiltered, expectedResources})
|
||||
ImmutableList.of(new Object[]{expectedPlanWithMvfiltered, expectedResources, expectedAttributes})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -358,13 +361,13 @@ public class CalciteExplainQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"signature\":[{\"name\":\"v0\",\"type\":\"LONG\"}]"
|
||||
+ "}]";
|
||||
final String expectedResources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";
|
||||
|
||||
final String expectedAttributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
// Verify the query plan
|
||||
testQuery(
|
||||
explainSql,
|
||||
queryContext,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(new Object[]{expectedPlan, expectedResources})
|
||||
ImmutableList.of(new Object[]{expectedPlan, expectedResources, expectedAttributes})
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ import org.apache.druid.sql.calcite.filtration.Filtration;
|
|||
import org.apache.druid.sql.calcite.parser.DruidSqlInsert;
|
||||
import org.apache.druid.sql.calcite.planner.Calcites;
|
||||
import org.apache.druid.sql.calcite.planner.IngestHandler;
|
||||
import org.apache.druid.sql.calcite.planner.PlannerConfig;
|
||||
import org.apache.druid.sql.calcite.planner.PlannerContext;
|
||||
import org.apache.druid.sql.calcite.util.CalciteTests;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
|
@ -870,14 +869,30 @@ public class CalciteInsertDmlTest extends CalciteIngestionDmlTest
|
|||
)
|
||||
.build();
|
||||
|
||||
final String expectedExplanation =
|
||||
final String legacyExplanation =
|
||||
"DruidQueryRel(query=["
|
||||
+ queryJsonMapper.writeValueAsString(expectedQuery)
|
||||
+ "], signature=[{x:STRING, y:STRING, z:LONG}])\n";
|
||||
|
||||
final String explanation =
|
||||
"["
|
||||
+ "{\"query\":{\"queryType\":\"scan\","
|
||||
+ "\"dataSource\":{\"type\":\"external\",\"inputSource\":{\"type\":\"inline\",\"data\":\"a,b,1\\nc,d,2\\n\"},"
|
||||
+ "\"inputFormat\":{\"type\":\"csv\",\"columns\":[\"x\",\"y\",\"z\"]},"
|
||||
+ "\"signature\":[{\"name\":\"x\",\"type\":\"STRING\"},{\"name\":\"y\",\"type\":\"STRING\"},{\"name\":\"z\",\"type\":\"LONG\"}]},"
|
||||
+ "\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},"
|
||||
+ "\"resultFormat\":\"compactedList\",\"columns\":[\"x\",\"y\",\"z\"],\"legacy\":false,"
|
||||
+ "\"context\":{\"sqlInsertSegmentGranularity\":\"{\\\"type\\\":\\\"all\\\"}\",\"sqlQueryId\":\"dummy\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},"
|
||||
+ "\"granularity\":{\"type\":\"all\"}},\"signature\":[{\"name\":\"x\",\"type\":\"STRING\"},{\"name\":\"y\",\"type\":\"STRING\"},{\"name\":\"z\",\"type\":\"LONG\"}]"
|
||||
+ "}]";
|
||||
|
||||
final String resources = "[{\"name\":\"EXTERNAL\",\"type\":\"EXTERNAL\"},{\"name\":\"dst\",\"type\":\"DATASOURCE\"}]";
|
||||
final String attributes = "{\"statementType\":\"INSERT\",\"targetDataSource\":\"dst\"}";
|
||||
|
||||
|
||||
// Use testQuery for EXPLAIN (not testIngestionQuery).
|
||||
testQuery(
|
||||
PlannerConfig.builder().useNativeQueryExplain(false).build(),
|
||||
PLANNER_CONFIG_LEGACY_QUERY_EXPLAIN,
|
||||
ImmutableMap.of("sqlQueryId", "dummy"),
|
||||
Collections.emptyList(),
|
||||
StringUtils.format(
|
||||
|
@ -889,8 +904,33 @@ public class CalciteInsertDmlTest extends CalciteIngestionDmlTest
|
|||
new DefaultResultsVerifier(
|
||||
ImmutableList.of(
|
||||
new Object[]{
|
||||
expectedExplanation,
|
||||
"[{\"name\":\"EXTERNAL\",\"type\":\"EXTERNAL\"},{\"name\":\"dst\",\"type\":\"DATASOURCE\"}]"
|
||||
legacyExplanation,
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
),
|
||||
null
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
|
||||
testQuery(
|
||||
PLANNER_CONFIG_NATIVE_QUERY_EXPLAIN,
|
||||
ImmutableMap.of("sqlQueryId", "dummy"),
|
||||
Collections.emptyList(),
|
||||
StringUtils.format(
|
||||
"EXPLAIN PLAN FOR INSERT INTO dst SELECT * FROM %s PARTITIONED BY ALL TIME",
|
||||
externSql(externalDataSource)
|
||||
),
|
||||
CalciteTests.SUPER_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
new DefaultResultsVerifier(
|
||||
ImmutableList.of(
|
||||
new Object[]{
|
||||
explanation,
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
),
|
||||
null
|
||||
|
|
|
@ -39,7 +39,6 @@ import org.apache.druid.sql.calcite.filtration.Filtration;
|
|||
import org.apache.druid.sql.calcite.parser.DruidSqlInsert;
|
||||
import org.apache.druid.sql.calcite.parser.DruidSqlParserUtils;
|
||||
import org.apache.druid.sql.calcite.parser.DruidSqlReplace;
|
||||
import org.apache.druid.sql.calcite.planner.PlannerConfig;
|
||||
import org.apache.druid.sql.calcite.planner.PlannerContext;
|
||||
import org.apache.druid.sql.calcite.util.CalciteTests;
|
||||
import org.junit.Assert;
|
||||
|
@ -614,14 +613,28 @@ public class CalciteReplaceDmlTest extends CalciteIngestionDmlTest
|
|||
)
|
||||
.build();
|
||||
|
||||
final String expectedExplanation =
|
||||
final String legacyExplanation =
|
||||
"DruidQueryRel(query=["
|
||||
+ queryJsonMapper.writeValueAsString(expectedQuery)
|
||||
+ "], signature=[{x:STRING, y:STRING, z:LONG}])\n";
|
||||
|
||||
final String explanation = "[{"
|
||||
+ "\"query\":{\"queryType\":\"scan\","
|
||||
+ "\"dataSource\":{\"type\":\"external\",\"inputSource\":{\"type\":\"inline\",\"data\":\"a,b,1\\nc,d,2\\n\"},"
|
||||
+ "\"inputFormat\":{\"type\":\"csv\",\"columns\":[\"x\",\"y\",\"z\"]},"
|
||||
+ "\"signature\":[{\"name\":\"x\",\"type\":\"STRING\"},{\"name\":\"y\",\"type\":\"STRING\"},{\"name\":\"z\",\"type\":\"LONG\"}]},"
|
||||
+ "\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},"
|
||||
+ "\"resultFormat\":\"compactedList\",\"columns\":[\"x\",\"y\",\"z\"],\"legacy\":false,"
|
||||
+ "\"context\":{\"sqlInsertSegmentGranularity\":\"{\\\"type\\\":\\\"all\\\"}\",\"sqlQueryId\":\"dummy\","
|
||||
+ "\"sqlReplaceTimeChunks\":\"all\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},\"granularity\":{\"type\":\"all\"}},"
|
||||
+ "\"signature\":[{\"name\":\"x\",\"type\":\"STRING\"},{\"name\":\"y\",\"type\":\"STRING\"},{\"name\":\"z\",\"type\":\"LONG\"}]}]";
|
||||
|
||||
final String resources = "[{\"name\":\"EXTERNAL\",\"type\":\"EXTERNAL\"},{\"name\":\"dst\",\"type\":\"DATASOURCE\"}]";
|
||||
final String attributes = "{\"statementType\":\"REPLACE\",\"targetDataSource\":\"dst\"}";
|
||||
|
||||
// Use testQuery for EXPLAIN (not testIngestionQuery).
|
||||
testQuery(
|
||||
PlannerConfig.builder().useNativeQueryExplain(false).build(),
|
||||
PLANNER_CONFIG_LEGACY_QUERY_EXPLAIN,
|
||||
ImmutableMap.of("sqlQueryId", "dummy"),
|
||||
Collections.emptyList(),
|
||||
StringUtils.format(
|
||||
|
@ -633,8 +646,32 @@ public class CalciteReplaceDmlTest extends CalciteIngestionDmlTest
|
|||
new DefaultResultsVerifier(
|
||||
ImmutableList.of(
|
||||
new Object[]{
|
||||
expectedExplanation,
|
||||
"[{\"name\":\"EXTERNAL\",\"type\":\"EXTERNAL\"},{\"name\":\"dst\",\"type\":\"DATASOURCE\"}]"
|
||||
legacyExplanation,
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
),
|
||||
null
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
testQuery(
|
||||
PLANNER_CONFIG_NATIVE_QUERY_EXPLAIN,
|
||||
ImmutableMap.of("sqlQueryId", "dummy"),
|
||||
Collections.emptyList(),
|
||||
StringUtils.format(
|
||||
"EXPLAIN PLAN FOR REPLACE INTO dst OVERWRITE ALL SELECT * FROM %s PARTITIONED BY ALL TIME",
|
||||
externSql(externalDataSource)
|
||||
),
|
||||
CalciteTests.SUPER_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
new DefaultResultsVerifier(
|
||||
ImmutableList.of(
|
||||
new Object[]{
|
||||
explanation,
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
),
|
||||
null
|
||||
|
|
|
@ -45,7 +45,6 @@ import org.apache.druid.segment.column.RowSignature;
|
|||
import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
|
||||
import org.apache.druid.sql.SqlPlanningException;
|
||||
import org.apache.druid.sql.calcite.filtration.Filtration;
|
||||
import org.apache.druid.sql.calcite.planner.PlannerConfig;
|
||||
import org.apache.druid.sql.calcite.planner.PlannerContext;
|
||||
import org.apache.druid.sql.calcite.util.CalciteTests;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -544,16 +543,18 @@ public class CalciteSelectQueryTest extends BaseCalciteQueryTest
|
|||
+ "}]";
|
||||
final String legacyExplanation = "DruidQueryRel(query=[{\"queryType\":\"scan\",\"dataSource\":{\"type\":\"inline\",\"columnNames\":[\"EXPR$0\"],\"columnTypes\":[\"LONG\"],\"rows\":[[2]]},\"intervals\":{\"type\":\"intervals\",\"intervals\":[\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\"]},\"resultFormat\":\"compactedList\",\"columns\":[\"EXPR$0\"],\"legacy\":false,\"context\":{\"defaultTimeout\":300000,\"maxScatterGatherBytes\":9223372036854775807,\"sqlCurrentTimestamp\":\"2000-01-01T00:00:00Z\",\"sqlQueryId\":\"dummy\",\"vectorize\":\"false\",\"vectorizeVirtualColumns\":\"false\"},\"granularity\":{\"type\":\"all\"}}], signature=[{EXPR$0:LONG}])\n";
|
||||
final String resources = "[]";
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
|
||||
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
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -565,7 +566,8 @@ public class CalciteSelectQueryTest extends BaseCalciteQueryTest
|
|||
ImmutableList.of(
|
||||
new Object[]{
|
||||
explanation,
|
||||
resources
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -1284,19 +1286,20 @@ public class CalciteSelectQueryTest extends BaseCalciteQueryTest
|
|||
+ "\"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>\"}]}]";
|
||||
final String resources = "[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]";
|
||||
final String attributes = "{\"statementType\":\"SELECT\",\"targetDataSource\":null}";
|
||||
|
||||
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
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -1308,7 +1311,8 @@ public class CalciteSelectQueryTest extends BaseCalciteQueryTest
|
|||
ImmutableList.of(
|
||||
new Object[]{
|
||||
explanation,
|
||||
resources
|
||||
resources,
|
||||
attributes
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
@ -317,6 +317,7 @@ public class IngestTableFunctionTest extends CalciteIngestionDmlTest
|
|||
"\"granularity\":{\"type\":\"all\"}}," +
|
||||
"\"signature\":[{\"name\":\"x\",\"type\":\"STRING\"},{\"name\":\"y\",\"type\":\"STRING\"},{\"name\":\"z\",\"type\":\"LONG\"}]}]";
|
||||
final String resources = "[{\"name\":\"EXTERNAL\",\"type\":\"EXTERNAL\"},{\"name\":\"dst\",\"type\":\"DATASOURCE\"}]";
|
||||
final String attributes = "{\"statementType\":\"INSERT\",\"targetDataSource\":\"dst\"}";
|
||||
|
||||
testQuery(
|
||||
PLANNER_CONFIG_NATIVE_QUERY_EXPLAIN,
|
||||
|
@ -324,7 +325,7 @@ public class IngestTableFunctionTest extends CalciteIngestionDmlTest
|
|||
CalciteTests.SUPER_USER_AUTH_RESULT,
|
||||
ImmutableList.of(),
|
||||
ImmutableList.of(
|
||||
new Object[]{explanation, resources}
|
||||
new Object[]{explanation, resources, attributes}
|
||||
)
|
||||
);
|
||||
didTest = true;
|
||||
|
|
|
@ -1326,7 +1326,9 @@ public class SqlResourceTest extends CalciteTestBase
|
|||
"false"
|
||||
),
|
||||
"RESOURCES",
|
||||
"[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]"
|
||||
"[{\"name\":\"foo\",\"type\":\"DATASOURCE\"}]",
|
||||
"ATTRIBUTES",
|
||||
"{\"statementType\":\"SELECT\",\"targetDataSource\":null}"
|
||||
)
|
||||
),
|
||||
rows
|
||||
|
|
Loading…
Reference in New Issue