Skip to content

Enhancement #3028: Add verbosity-level option to SQL EXPLAIN #3485

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

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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 @@ -722,6 +722,15 @@ public static String toStringForExternalExplain(@Nonnull final RecordQueryPlan p
return toStringForExternalExplain(plan, ExplainLevel.ALL_DETAILS, Integer.MAX_VALUE);
}

@Nonnull
public static String toStringForExternalExplain(@Nonnull final RecordQueryPlan plan, final boolean isVerboseExplainLevel) {
final var visitor = new ExplainPlanVisitor(Integer.MAX_VALUE);
final var explainTokens = visitor.visit(plan);
final var explainLevel = isVerboseExplainLevel ? ExplainLevel.ALL_DETAILS : ExplainLevel.STRUCTURE;
return explainTokens.render(explainLevel, new DefaultExplainFormatter(ExplainSelfContainedSymbolMap::new), explainTokens.getMaxLength(explainLevel))
.toString();
}

@Nonnull
public static String toStringForExternalExplain(@Nonnull final RecordQueryPlan plan, final int maxExplainLevel,
final int maxSize) {
Expand Down
3 changes: 2 additions & 1 deletion fdb-relational-core/src/main/antlr/RelationalLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ USAGE: 'USAGE';
USE: 'USE';
USING: 'USING';
VALUES: 'VALUES';
VERBOSE: 'VERBOSE';
WHEN: 'WHEN';
WHERE: 'WHERE';
WHILE: 'WHILE';
Expand Down Expand Up @@ -1352,4 +1353,4 @@ fragment DECIMAL_TYPE_MODIFIER: (INT_TYPE_MODIFIER | LONG_TYPE_MODIFIER);

ERROR_RECOGNITION
: . { this.notifyListeners(new LexerNoViableAltException(this, _input, _tokenStartCharIndex, null)); }
;
;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@normen662 Done!

3 changes: 2 additions & 1 deletion fdb-relational-core/src/main/antlr/RelationalParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,8 @@ helpStatement
// details

describeObjectClause
: (
: (VERBOSE)?
(
query | deleteStatement | insertStatement
| updateStatement | executeContinuationStatement
) #describeStatements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@ public Void visitFullDescribeStatement(@Nonnull RelationalParser.FullDescribeSta
return visitChildren(ctx);
}

@Override
public Void visitDescribeStatements(@Nonnull RelationalParser.DescribeStatementsContext ctx) {
queryHasherContextBuilder.setIsVerboseExplainLevel(ctx.VERBOSE() != null);
return visitChildren(ctx);
}

@Override
public Void visitLimitClause(@Nonnull RelationalParser.LimitClauseContext ctx) {
if (ctx.offset != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package com.apple.foundationdb.relational.recordlayer.query;

import com.apple.foundationdb.annotation.API;

import com.apple.foundationdb.record.ExecuteProperties;
import com.apple.foundationdb.record.PlanHashable;
import com.apple.foundationdb.record.query.expressions.Comparisons;
Expand All @@ -43,7 +42,6 @@
import com.apple.foundationdb.relational.recordlayer.metadata.DataTypeUtils;
import com.apple.foundationdb.relational.util.Assert;
import com.apple.foundationdb.relational.util.SpotBugsSuppressWarnings;

import com.google.common.collect.ImmutableList;
import com.google.protobuf.ZeroCopyByteString;

Expand Down Expand Up @@ -91,6 +89,8 @@ public class MutablePlanGenerationContext implements QueryExecutionContext {

private boolean forExplain;

private boolean isVerboseExplainLevel;

@Nullable
private byte[] continuation;

Expand Down Expand Up @@ -279,6 +279,7 @@ public MutablePlanGenerationContext(@Nonnull PreparedParams preparedParams,
constantObjectValues = new LinkedList<>();
shouldProcessLiteral = true;
forExplain = false;
isVerboseExplainLevel = false;
setContinuation(null);
equalityConstraints = ImmutableList.builder();
}
Expand Down Expand Up @@ -354,6 +355,10 @@ public boolean isForExplain() {
return forExplain;
}

@Override
public boolean isVerboseExplainLevel() {
return isVerboseExplainLevel;
}

@Nonnull
public QueryPlanConstraint getPlanConstraintsForLiteralReferences() {
Expand Down Expand Up @@ -390,6 +395,10 @@ public void setForExplain(boolean forExplain) {
this.forExplain = forExplain;
}

public void setIsVerboseExplainLevel(boolean isVerboseExplainLevel) {
this.isVerboseExplainLevel = isVerboseExplainLevel;
}

@Nonnull
public Value processQueryLiteral(@Nonnull Type type, @Nullable Object literal, int tokenIndex) {
return processQueryLiteralOrParameter(type, literal, null, null, tokenIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public final class NormalizedQueryExecutionContext implements QueryExecutionCont

private final boolean isForExplain;

private final boolean isVerboseExplainLevel;

private final int parameterHash;

@Nonnull
Expand All @@ -53,10 +55,12 @@ private NormalizedQueryExecutionContext(@Nonnull Literals literals,
@Nullable byte[] continuation,
int parameterHash,
boolean isForExplain,
boolean isVerboseExplainLevel,
@Nonnull final PlanHashable.PlanHashMode planHashMode) {
this.literals = literals;
this.continuation = continuation;
this.isForExplain = isForExplain;
this.isVerboseExplainLevel = isVerboseExplainLevel;
this.parameterHash = parameterHash;
this.planHashMode = planHashMode;
}
Expand Down Expand Up @@ -90,6 +94,11 @@ public boolean isForExplain() {
return isForExplain;
}

@Override
public boolean isVerboseExplainLevel() {
return isVerboseExplainLevel;
}

@Nonnull
@Override
public PlanHashable.PlanHashMode getPlanHashMode() {
Expand All @@ -107,6 +116,8 @@ public static final class Builder {

private boolean isForExplain;

private boolean isVerboseExplainLevel;

@Nullable
private byte[] continuation;

Expand All @@ -120,6 +131,7 @@ private Builder() {
this.isForExplain = false;
this.continuation = null;
this.planHashMode = null;
this.isVerboseExplainLevel = false;
}

@Nonnull
Expand All @@ -146,6 +158,12 @@ public Builder setForExplain(boolean isForExplain) {
return this;
}

@Nonnull
public Builder setIsVerboseExplainLevel(boolean isVerboseExplainLevel) {
this.isVerboseExplainLevel = isVerboseExplainLevel;
return this;
}

@Nonnull
public Builder setPlanHashMode(@Nonnull PlanHashable.PlanHashMode planHashMode) {
this.planHashMode = planHashMode;
Expand All @@ -155,7 +173,7 @@ public Builder setPlanHashMode(@Nonnull PlanHashable.PlanHashMode planHashMode)
@Nonnull
public NormalizedQueryExecutionContext build() {
return new NormalizedQueryExecutionContext(literalsBuilder.build(), continuation,
parameterHash, isForExplain,
parameterHash, isForExplain, isVerboseExplainLevel,
Objects.requireNonNull(planHashMode));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ default EvaluationContext getEvaluationContext() {

boolean isForExplain(); // todo (yhatem) remove.

boolean isVerboseExplainLevel();

@Nonnull
PlanHashable.PlanHashMode getPlanHashMode();
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public PhysicalQueryPlan withExecutionContext(@Nonnull final QueryExecutionConte
public String explain() {
final var executeProperties = queryExecutionContext.getExecutionPropertiesBuilder();
List<String> explainComponents = new ArrayList<>();
explainComponents.add(ExplainPlanVisitor.toStringForExternalExplain(recordQueryPlan));
explainComponents.add(ExplainPlanVisitor.toStringForExternalExplain(recordQueryPlan, queryExecutionContext.isVerboseExplainLevel()));
if (executeProperties.getReturnedRowLimit() != ReadTransaction.ROW_LIMIT_UNLIMITED) {
explainComponents.add("(limit=" + executeProperties.getReturnedRowLimit() + ")");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,11 @@ public Object visitExecuteContinuationStatement(@Nonnull RelationalParser.Execut
@Override
public QueryPlan.LogicalQueryPlan visitFullDescribeStatement(@Nonnull RelationalParser.FullDescribeStatementContext ctx) {
getDelegate().getPlanGenerationContext().setForExplain(ctx.EXPLAIN() != null);
if (!ctx.describeObjectClause().getTokens(RelationalLexer.VERBOSE).isEmpty()) {
getDelegate().getPlanGenerationContext().setIsVerboseExplainLevel(true);
} else {
getDelegate().getPlanGenerationContext().setIsVerboseExplainLevel(false);
}
final var logicalOperator = Assert.castUnchecked(ctx.describeObjectClause().accept(this), LogicalOperator.class);
return QueryPlan.LogicalQueryPlan.of(logicalOperator.getQuantifier().getRangesOver().get(), getDelegate().getPlanGenerationContext(), "TODO");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void explainWithNoContinuationTest() throws Exception {
try (final RelationalResultSet resultSet = ps.executeQuery()) {
final var assertResult = ResultSetAssert.assertThat(resultSet);
assertResult.hasNextRow()
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX <,>)")
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX)")
.hasColumn("PLAN_HASH", -1635569052L)
.hasColumn("PLAN_CONTINUATION", null);
assertResult.hasNoNextRow();
Expand All @@ -128,7 +128,7 @@ void explainWithContinuationNoSerializedPlanTest() throws Exception {
try (final RelationalResultSet resultSet = ps.executeQuery()) {
final var assertResult = ResultSetAssert.assertThat(resultSet);
assertResult.hasNextRow()
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX <,>)")
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX)")
.hasColumn("PLAN_HASH", -1635569052L);
final var continuationInfo = resultSet.getStruct(5);
org.junit.jupiter.api.Assertions.assertNotNull(continuationInfo);
Expand Down Expand Up @@ -159,7 +159,7 @@ void explainWithContinuationSerializedPlanTest() throws Exception {
try (final RelationalResultSet resultSet = ps.executeQuery()) {
final var assertResult = ResultSetAssert.assertThat(resultSet);
assertResult.hasNextRow()
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX <,>)")
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX)")
.hasColumn("PLAN_HASH", -1635569052L);
final var continuationInfo = resultSet.getStruct(5);
org.junit.jupiter.api.Assertions.assertNotNull(continuationInfo);
Expand Down Expand Up @@ -190,7 +190,7 @@ void explainExecuteStatementTest() throws Exception {
try (final RelationalResultSet resultSet = ps.executeQuery()) {
final var assertResult = ResultSetAssert.assertThat(resultSet);
assertResult.hasNextRow()
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX <,>)")
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX)")
.hasColumn("PLAN_HASH", -1635569052L);
final var continuationInfo = resultSet.getStruct(5);
org.junit.jupiter.api.Assertions.assertNotNull(continuationInfo);
Expand All @@ -205,6 +205,42 @@ void explainExecuteStatementTest() throws Exception {
}
}

@Test
void explainVerboseTest() throws Exception {
try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) {
executeInsert(ddl);
try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("EXPLAIN VERBOSE SELECT * FROM RestaurantComplexRecord where COUNT(reviews) > 3 and COUNT(reviews) < 100")) {
ps.setMaxRows(2);
try (final RelationalResultSet resultSet = ps.executeQuery()) {
final var assertResult = ResultSetAssert.assertThat(resultSet);
assertResult.hasNextRow()
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX <,>) | FILTER count_star(*) GREATER_THAN promote(@c12 AS LONG) AND count_star(*) LESS_THAN promote(@c19 AS LONG)")
.hasColumn("PLAN_HASH", -1697137247L)
.hasColumn("PLAN_CONTINUATION", null);
assertResult.hasNoNextRow();
}
}
}
}

@Test
void explainDefaultTest() throws Exception {
try (var ddl = Ddl.builder().database(URI.create("/TEST/QT")).relationalExtension(relationalExtension).schemaTemplate(schemaTemplate).build()) {
executeInsert(ddl);
try (RelationalPreparedStatement ps = ddl.setSchemaAndGetConnection().prepareStatement("EXPLAIN SELECT * FROM RestaurantComplexRecord where COUNT(reviews) > 3 and COUNT(reviews) < 100")) {
ps.setMaxRows(2);
try (final RelationalResultSet resultSet = ps.executeQuery()) {
final var assertResult = ResultSetAssert.assertThat(resultSet);
assertResult.hasNextRow()
.hasColumn("PLAN", "ISCAN(RECORD_NAME_IDX) | FILTER count_star(*) GREATER_THAN promote(@c11 AS LONG) AND count_star(*) LESS_THAN promote(@c18 AS LONG)")
.hasColumn("PLAN_HASH", -1697137247L)
.hasColumn("PLAN_CONTINUATION", null);
assertResult.hasNoNextRow();
}
}
}
}

private Continuation consumeResultAndGetContinuation(RelationalPreparedStatement ps, int numRows) throws SQLException {
Continuation continuation;
try (final RelationalResultSet resultSet = ps.executeQuery()) {
Expand Down