github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optgen/exprgen/private.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 exprgen
    12  
    13  import (
    14  	"context"
    15  	"encoding/json"
    16  	"reflect"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/opt/optgen/lang"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/stats"
    28  )
    29  
    30  // evalPrivate evaluates a list of the form
    31  //   [ (FieldName <value>) ... ]
    32  // into an operation private of the given type (e.g. ScanPrivate, etc).
    33  //
    34  // Various implicit conversions are supported. Examples:
    35  //  - table ID: "table"
    36  //  - index ordinal: "table@index"
    37  //  - column lists or sets: "a,b,c"
    38  //  - orderings and ordering choices: "+a,-b"
    39  //  - operators: "inner-join"
    40  //
    41  func (eg *exprGen) evalPrivate(privType reflect.Type, expr lang.Expr) interface{} {
    42  	if expr.Op() != lang.ListOp {
    43  		panic(errorf("private must be a list of the form [ (FieldName Value) ... ]"))
    44  	}
    45  
    46  	// Special case for FakeRelPrivate; we want to specify the Relational fields
    47  	// directly.
    48  	if privType == reflect.TypeOf(memo.FakeRelPrivate{}) {
    49  		props := eg.evalPrivate(reflect.TypeOf(props.Relational{}), expr).(*props.Relational)
    50  		return &memo.FakeRelPrivate{Props: props}
    51  	}
    52  
    53  	items := expr.(*lang.ListExpr).Items
    54  
    55  	result := reflect.New(privType)
    56  
    57  	for _, item := range items {
    58  		// Each item must be of the form (FieldName Value).
    59  		fn, ok := item.(*lang.FuncExpr)
    60  		if !ok || len(fn.Args) != 1 {
    61  			panic(errorf("private list must contain items of the form (FieldName Value)"))
    62  		}
    63  		fieldName := fn.SingleName()
    64  		field := result.Elem().FieldByName(fieldName)
    65  		if !field.IsValid() {
    66  			panic(errorf("invalid field %s for %s", fieldName, privType))
    67  		}
    68  		val := eg.convertPrivateFieldValue(privType, fieldName, field.Type(), eg.eval(fn.Args[0]))
    69  		field.Set(reflect.ValueOf(val))
    70  	}
    71  	return result.Interface()
    72  }
    73  
    74  func (eg *exprGen) convertPrivateFieldValue(
    75  	privType reflect.Type, fieldName string, fieldType reflect.Type, value interface{},
    76  ) interface{} {
    77  
    78  	// This code handles the conversion of a user-friendly value and the value of
    79  	// the field in the private structure.
    80  
    81  	if str, ok := value.(string); ok {
    82  		switch fieldType {
    83  		case reflect.TypeOf(opt.TableID(0)):
    84  			return eg.addTable(str)
    85  
    86  		case reflect.TypeOf(0):
    87  			if strings.HasSuffix(fieldName, "Index") {
    88  				return eg.findIndex(str)
    89  			}
    90  
    91  		case reflect.TypeOf(opt.Operator(0)):
    92  			return eg.opFromStr(str)
    93  
    94  		case reflect.TypeOf(props.Cardinality{}):
    95  			return eg.cardinalityFromStr(str)
    96  
    97  		case reflect.TypeOf(props.Statistics{}):
    98  			return eg.statsFromStr(str)
    99  		}
   100  	}
   101  
   102  	if res := eg.castToDesiredType(value, fieldType); res != nil {
   103  		return res
   104  	}
   105  	panic(errorf("invalid value for %s.%s: %v", privType, fieldName, value))
   106  }
   107  
   108  // addTable resolves the given table name and adds the table to the metadata.
   109  func (eg *exprGen) addTable(name string) opt.TableID {
   110  	tn := tree.MakeUnqualifiedTableName(tree.Name(name))
   111  	ds, _, err := eg.cat.ResolveDataSource(context.Background(), cat.Flags{}, &tn)
   112  	if err != nil {
   113  		panic(exprGenErr{err})
   114  	}
   115  	tab, ok := ds.(cat.Table)
   116  	if !ok {
   117  		panic(errorf("non-table datasource %s not supported", name))
   118  	}
   119  	return eg.mem.Metadata().AddTable(tab, &tn)
   120  }
   121  
   122  // findIndex looks for an index specified as "table@idx_name" among the tables
   123  // already added to the metadata.
   124  func (eg *exprGen) findIndex(str string) int {
   125  	a := strings.Split(str, "@")
   126  	if len(a) != 2 {
   127  		panic(errorf("index must be specified as table@index"))
   128  	}
   129  	table, index := a[0], a[1]
   130  	var tab cat.Table
   131  	for _, meta := range eg.mem.Metadata().AllTables() {
   132  		if meta.Alias.Table() == table {
   133  			if tab != nil {
   134  				panic(errorf("ambiguous table name %s", table))
   135  			}
   136  			tab = meta.Table
   137  		}
   138  	}
   139  	if tab == nil {
   140  		panic(errorf("unknown table %s", table))
   141  	}
   142  	for i := 0; i < tab.IndexCount(); i++ {
   143  		if string(tab.Index(i).Name()) == index {
   144  			return i
   145  		}
   146  	}
   147  	panic(errorf("index %s not found for table %s", index, table))
   148  }
   149  
   150  // opFromStr converts an operator string like "inner-join" to the corresponding
   151  // operator.
   152  func (eg *exprGen) opFromStr(str string) opt.Operator {
   153  	for i := opt.Operator(1); i < opt.NumOperators; i++ {
   154  		if i.String() == str {
   155  			return i
   156  		}
   157  	}
   158  	panic(errorf("unknown operator %s", str))
   159  }
   160  
   161  func (eg *exprGen) cardinalityFromStr(str string) props.Cardinality {
   162  	pieces := strings.SplitN(str, "-", 2)
   163  	if len(pieces) != 2 {
   164  		panic(errorf("cardinality must be of the form \"[<min>] - [<max>]\": %s", str))
   165  	}
   166  	a := strings.Trim(pieces[0], " ")
   167  	b := strings.Trim(pieces[1], " ")
   168  	c := props.AnyCardinality
   169  	if a != "" {
   170  		c.Min = uint32(eg.intFromStr(a))
   171  	}
   172  	if b != "" {
   173  		c.Max = uint32(eg.intFromStr(b))
   174  	}
   175  	return c
   176  }
   177  
   178  func (eg *exprGen) intFromStr(str string) int {
   179  	val, err := strconv.Atoi(str)
   180  	if err != nil {
   181  		panic(wrapf(err, "expected number: %s", str))
   182  	}
   183  	return val
   184  }
   185  
   186  func (eg *exprGen) statsFromStr(str string) props.Statistics {
   187  	var stats []stats.JSONStatistic
   188  	if err := json.Unmarshal([]byte(str), &stats); err != nil {
   189  		panic(wrapf(err, "error unmarshaling statistics"))
   190  	}
   191  	var result props.Statistics
   192  	if len(stats) == 0 {
   193  		return result
   194  	}
   195  	// Sort the statistics, most-recent first.
   196  	sort.Slice(stats, func(i, j int) bool {
   197  		return stats[i].CreatedAt > stats[j].CreatedAt
   198  	})
   199  	result.RowCount = float64(stats[0].RowCount)
   200  	for i := range stats {
   201  		var cols opt.ColSet
   202  		for _, colStr := range stats[i].Columns {
   203  			cols.Add(eg.LookupColumn(colStr))
   204  		}
   205  		s, added := result.ColStats.Add(cols)
   206  		if !added {
   207  			// The same set was already in a more recent statistic, ignore.
   208  			continue
   209  		}
   210  		s.DistinctCount = float64(stats[i].DistinctCount)
   211  		s.NullCount = float64(stats[i].NullCount)
   212  	}
   213  	return result
   214  }