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 }