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

     1  // Copyright 2019 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 delegate
    12  
    13  import (
    14  	"encoding/hex"
    15  	"fmt"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/privilege"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
    24  	"github.com/cockroachdb/errors"
    25  )
    26  
    27  func (d *delegator) delegateShowRangeForRow(n *tree.ShowRangeForRow) (tree.Statement, error) {
    28  	flags := cat.Flags{AvoidDescriptorCaches: true}
    29  	idx, resName, err := cat.ResolveTableIndex(d.ctx, d.catalog, flags, &n.TableOrIndex)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	if err := d.catalog.CheckPrivilege(d.ctx, idx.Table(), privilege.SELECT); err != nil {
    34  		return nil, err
    35  	}
    36  	if idx.Table().IsVirtualTable() {
    37  		return nil, errors.New("SHOW RANGE FOR ROW may not be called on a virtual table")
    38  	}
    39  	span := idx.Span()
    40  	table := idx.Table()
    41  
    42  	if len(n.Row) != table.ColumnCount() {
    43  		return nil, errors.New("number of values in row must equal number of columns in the requested table")
    44  	}
    45  
    46  	// Process the Datums within the expressions.
    47  	var semaCtx tree.SemaContext
    48  	var rowExprs tree.Exprs
    49  	for i, expr := range n.Row {
    50  		colTyp := table.Column(i).DatumType()
    51  		typedExpr, err := sqlbase.SanitizeVarFreeExpr(d.ctx, expr, colTyp, "range-for-row", &semaCtx, false)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		if !tree.IsConst(d.evalCtx, typedExpr) {
    56  			return nil, pgerror.Newf(pgcode.Syntax, "%s: row values must be constant", typedExpr)
    57  		}
    58  		datum, err := typedExpr.Eval(d.evalCtx)
    59  		if err != nil {
    60  			return nil, errors.Wrapf(err, "%s", typedExpr)
    61  		}
    62  		rowExprs = append(rowExprs, datum)
    63  	}
    64  
    65  	idxSpanStart := hex.EncodeToString([]byte(span.Key))
    66  	idxSpanEnd := hex.EncodeToString([]byte(span.EndKey))
    67  
    68  	sqltelemetry.IncrementShowCounter(sqltelemetry.RangeForRow)
    69  
    70  	// Format the expressions into a string to be passed into the crdb_internal.encode_key function.
    71  	// We have to be sneaky here and special case when exprs has length 1 and place a comma after the
    72  	// the single tuple element so that we can deduce the expression actually has a tuple type for
    73  	// the crdb_internal.encode_key function.
    74  	// Example: exprs = (1)
    75  	// Output when used: crdb_internal.encode_key(x, y, (1,))
    76  	var fmtCtx tree.FmtCtx
    77  	fmtCtx.WriteString("(")
    78  	if len(rowExprs) == 1 {
    79  		fmtCtx.FormatNode(rowExprs[0])
    80  		fmtCtx.WriteString(",")
    81  	} else {
    82  		fmtCtx.FormatNode(&rowExprs)
    83  	}
    84  	fmtCtx.WriteString(")")
    85  	rowString := fmtCtx.String()
    86  
    87  	const query = `
    88  SELECT
    89  	CASE WHEN r.start_key < x'%[5]s' THEN NULL ELSE crdb_internal.pretty_key(r.start_key, 2) END AS start_key,
    90  	CASE WHEN r.end_key >= x'%[6]s' THEN NULL ELSE crdb_internal.pretty_key(r.end_key, 2) END AS end_key,
    91  	range_id,
    92  	lease_holder,
    93  	gossip_nodes.locality as lease_holder_locality,
    94  	replicas,
    95  	replica_localities
    96  FROM %[4]s.crdb_internal.ranges AS r
    97  LEFT JOIN %[4]s.crdb_internal.gossip_nodes ON lease_holder = node_id
    98  WHERE (r.start_key <= crdb_internal.encode_key(%[1]d, %[2]d, %[3]s))
    99    AND (r.end_key   >  crdb_internal.encode_key(%[1]d, %[2]d, %[3]s)) ORDER BY r.start_key
   100  	`
   101  	// note: CatalogName.String() != Catalog()
   102  	return parse(
   103  		fmt.Sprintf(
   104  			query,
   105  			table.ID(),
   106  			idx.ID(),
   107  			rowString,
   108  			resName.CatalogName.String(),
   109  			idxSpanStart,
   110  			idxSpanEnd,
   111  		),
   112  	)
   113  
   114  }