-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
show_histogram.go
102 lines (94 loc) · 3.3 KB
/
show_histogram.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright 2017 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package sql
import (
"context"
"fmt"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descs"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
"github.com/cockroachdb/cockroach/pkg/sql/stats"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util/protoutil"
"github.com/cockroachdb/errors"
)
// Ideally, we would want upper_bound to have the type of the column the
// histogram is on. However, we don't want to have a SHOW statement for which
// the schema depends on its parameters.
var showHistogramColumns = colinfo.ResultColumns{
{Name: "upper_bound", Typ: types.String},
{Name: "range_rows", Typ: types.Int},
{Name: "distinct_range_rows", Typ: types.Float},
{Name: "equal_rows", Typ: types.Int},
}
// ShowHistogram returns a SHOW HISTOGRAM statement.
// Privileges: Any privilege on the respective table.
func (p *planner) ShowHistogram(ctx context.Context, n *tree.ShowHistogram) (planNode, error) {
return &delayedNode{
name: fmt.Sprintf("SHOW HISTOGRAM %d", n.HistogramID),
columns: showHistogramColumns,
constructor: func(ctx context.Context, p *planner) (planNode, error) {
row, err := p.InternalSQLTxn().QueryRowEx(
ctx,
"read-histogram",
p.txn,
sessiondata.RootUserSessionDataOverride,
`SELECT histogram
FROM system.table_statistics
WHERE "statisticID" = $1`,
n.HistogramID,
)
if err != nil {
return nil, err
}
if row == nil {
return nil, fmt.Errorf("histogram %d not found", n.HistogramID)
}
if len(row) != 1 {
return nil, errors.AssertionFailedf("expected 1 column from internal query")
}
if row[0] == tree.DNull {
// We found a statistic, but it has no histogram.
return nil, fmt.Errorf("histogram %d not found", n.HistogramID)
}
histogram := &stats.HistogramData{}
histData := *row[0].(*tree.DBytes)
if err := protoutil.Unmarshal([]byte(histData), histogram); err != nil {
return nil, err
}
v := p.newContainerValuesNode(showHistogramColumns, len(histogram.Buckets))
resolver := descs.NewDistSQLTypeResolver(p.descCollection, p.InternalSQLTxn().KV())
if err := typedesc.EnsureTypeIsHydrated(ctx, histogram.ColumnType, &resolver); err != nil {
return nil, err
}
var a tree.DatumAlloc
for _, b := range histogram.Buckets {
datum, err := stats.DecodeUpperBound(histogram.Version, histogram.ColumnType, &a, b.UpperBound)
if err != nil {
v.Close(ctx)
return nil, err
}
row := tree.Datums{
tree.NewDString(datum.String()),
tree.NewDInt(tree.DInt(b.NumRange)),
tree.NewDFloat(tree.DFloat(b.DistinctRange)),
tree.NewDInt(tree.DInt(b.NumEq)),
}
if _, err := v.rows.AddRow(ctx, row); err != nil {
v.Close(ctx)
return nil, err
}
}
return v, nil
},
}, nil
}