github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/show_histogram.go (about)

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