diff --git a/pkg/server/combined_statement_stats.go b/pkg/server/combined_statement_stats.go index 91871b4cc7b6..fb8bec52ba65 100644 --- a/pkg/server/combined_statement_stats.go +++ b/pkg/server/combined_statement_stats.go @@ -729,8 +729,11 @@ func getStatementDetailsPerPlanHash( if planHash, err = sqlstatsutil.DatumToUint64(row[0]); err != nil { return nil, serverError(ctx, err) } - planGist := string(tree.MustBeDString(row[1])) - explainPlan := getExplainPlanFromGist(ctx, ie, planGist) + planGist := string(tree.MustBeDStringOrDNull(row[1])) + var explainPlan string + if planGist != "" { + explainPlan = getExplainPlanFromGist(ctx, ie, planGist) + } var metadata roachpb.CollectedStatementStatistics var aggregatedMetadata roachpb.AggregatedStatementMetadata diff --git a/pkg/sql/sem/tree/datum.go b/pkg/sql/sem/tree/datum.go index c26f62156090..d717b31554fa 100644 --- a/pkg/sql/sem/tree/datum.go +++ b/pkg/sql/sem/tree/datum.go @@ -1220,6 +1220,16 @@ func MustBeDString(e Expr) DString { return i } +// MustBeDStringOrDNull attempts to retrieve a DString or DNull from an Expr, panicking if the +// assertion fails. +func MustBeDStringOrDNull(e Expr) DString { + i, ok := AsDString(e) + if !ok && e != DNull { + panic(errors.AssertionFailedf("expected *DString or DNull, found %T", e)) + } + return i +} + // ResolvedType implements the TypedExpr interface. func (*DString) ResolvedType() *types.T { return types.String diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx index 42693aa2a223..e7e04f8aa05b 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/planDetails/planDetails.tsx @@ -57,6 +57,8 @@ function renderExplainPlan( plan: PlanHashStats, backToPlanTable: () => void, ): React.ReactElement { + const explainPlan = + plan.explain_plan === "" ? "unavailable" : plan.explain_plan; return (