github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optgen/exprgen/custom_funcs.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 "fmt" 15 "strings" 16 "unicode" 17 18 "github.com/cockroachdb/cockroach/pkg/sql/opt" 19 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 20 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 21 "github.com/cockroachdb/cockroach/pkg/sql/opt/norm" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 ) 25 26 type customFuncs struct { 27 f *norm.Factory 28 mem *memo.Memo 29 cat cat.Catalog 30 } 31 32 // NewColumn creates a new column in the metadata. 33 func (c *customFuncs) NewColumn(name, typeStr string) opt.ColumnID { 34 typ, err := ParseType(typeStr) 35 if err != nil { 36 panic(exprGenErr{err}) 37 } 38 return c.f.Metadata().AddColumn(name, typ) 39 } 40 41 // LookupColumn looks up a column that was already specified in the expression 42 // so far (either via NewColumn or by using a table). 43 func (c *customFuncs) LookupColumn(name string) opt.ColumnID { 44 md := c.f.Metadata() 45 46 var res opt.ColumnID 47 for colID := opt.ColumnID(1); int(colID) <= md.NumColumns(); colID++ { 48 if md.ColumnMeta(colID).Alias == name { 49 if res != 0 { 50 panic(errorf("ambiguous column %s", name)) 51 } 52 res = colID 53 } 54 } 55 if res == 0 { 56 panic(errorf("unknown column %s", name)) 57 } 58 return res 59 } 60 61 // ColList creates a ColList from a comma-separated list of column names, 62 // looking up each column. 63 func (c *customFuncs) ColList(cols string) opt.ColList { 64 strs := strings.Split(cols, ",") 65 res := make(opt.ColList, len(strs)) 66 for i, col := range strs { 67 res[i] = c.LookupColumn(col) 68 } 69 return res 70 } 71 72 // ColSet creates a ColSet from a comma-separated list of column names, looking 73 // up each column. 74 func (c *customFuncs) ColSet(cols string) opt.ColSet { 75 return c.ColList(cols).ToSet() 76 } 77 78 // MinPhysProps returns the singleton minimum set of physical properties. 79 func (c *customFuncs) MinPhysProps() *physical.Required { 80 return physical.MinRequired 81 } 82 83 // MakePhysProps returns a set of physical properties corresponding to the 84 // input presentation and OrderingChoice. 85 func (c *customFuncs) MakePhysProps( 86 p physical.Presentation, o physical.OrderingChoice, 87 ) *physical.Required { 88 return c.mem.InternPhysicalProps(&physical.Required{ 89 Presentation: p, 90 Ordering: o, 91 }) 92 } 93 94 // ExplainOptions creates a tree.ExplainOptions from a comma-separated list of 95 // options. 96 func (c *customFuncs) ExplainOptions(opts string) tree.ExplainOptions { 97 explain, err := tree.MakeExplain(strings.Split(opts, ","), &tree.Select{}) 98 if err != nil { 99 panic(exprGenErr{err}) 100 } 101 return explain.(*tree.Explain).ExplainOptions 102 } 103 104 // Var creates a VariableOp for the given column. It allows (Var "name") as a 105 // shorthand for (Variable (LookupColumn "name")). 106 func (c *customFuncs) Var(colName string) opt.ScalarExpr { 107 return c.f.ConstructVariable(c.LookupColumn(colName)) 108 } 109 110 // FindTable looks up a table in the metadata without creating it. 111 // This is required to construct operators like IndexJoin which must 112 // reference the same table multiple times. 113 func (c *customFuncs) FindTable(name string) opt.TableID { 114 tables := c.mem.Metadata().AllTables() 115 116 var res opt.TableID 117 for i := range tables { 118 if string(tables[i].Table.Name()) == name { 119 if res != 0 { 120 panic(errorf("ambiguous table %q", name)) 121 } 122 res = tables[i].MetaID 123 } 124 } 125 if res == 0 { 126 panic(errorf("couldn't find table with name %q", name)) 127 } 128 return res 129 } 130 131 // Ordering parses a string like "+a,-b" into an Ordering. 132 func (c *customFuncs) Ordering(str string) opt.Ordering { 133 defer func() { 134 if r := recover(); r != nil { 135 panic(errorf("could not parse Ordering \"%s\"", str)) 136 } 137 }() 138 return physical.ParseOrdering(c.substituteCols(str)) 139 } 140 141 // OrderingChoice parses a string like "+a,-(b|c)" into an OrderingChoice. 142 func (c *customFuncs) OrderingChoice(str string) physical.OrderingChoice { 143 defer func() { 144 if r := recover(); r != nil { 145 panic(errorf("could not parse OrderingChoice \"%s\"", str)) 146 } 147 }() 148 return physical.ParseOrderingChoice(c.substituteCols(str)) 149 } 150 151 // substituteCols extracts every word (sequence of letters, numbers, and 152 // underscores) from the string, looks up the column with that name, and 153 // replaces the string with the column ID. E.g.: "+a,+b" -> "+1,+2". 154 func (c *customFuncs) substituteCols(str string) string { 155 var b strings.Builder 156 lastPos := -1 157 maybeEmit := func(curPos int) { 158 if lastPos != -1 { 159 col := str[lastPos:curPos] 160 fmt.Fprintf(&b, "%d", c.LookupColumn(col)) 161 } 162 lastPos = -1 163 } 164 for i, r := range str { 165 if unicode.IsLetter(r) || r == '_' || unicode.IsNumber(r) { 166 if lastPos == -1 { 167 lastPos = i 168 } 169 continue 170 } 171 maybeEmit(i) 172 b.WriteRune(r) 173 } 174 maybeEmit(len(str)) 175 return b.String() 176 } 177 178 // MakeLookupJoin is a wrapper around ConstructLookupJoin that swaps the order 179 // of the private and the filters. This is useful because the expressions are 180 // evaluated in order, and we want to be able to refer to the lookup columns in 181 // the ON expression. For example: 182 // 183 // (MakeLookupJoin 184 // (Scan [ (Table "def") (Cols "d,e") ]) 185 // [ (JoinType "left-join") (Table "abc") (Index "abc@ab") (KeyCols "a") (Cols "a,b") ] 186 // [ (Gt (Var "a") (Var "e")) ] 187 // ) 188 // 189 // If the order of the last two was swapped, we wouldn't be able to look up 190 // column a. 191 func (c *customFuncs) MakeLookupJoin( 192 input memo.RelExpr, lookupJoinPrivate *memo.LookupJoinPrivate, on memo.FiltersExpr, 193 ) memo.RelExpr { 194 return c.f.ConstructLookupJoin(input, on, lookupJoinPrivate) 195 } 196 197 // Sort adds a sort enforcer which sorts according to the ordering that will be 198 // required by its parent. 199 func (c *customFuncs) Sort(input memo.RelExpr) memo.RelExpr { 200 return &memo.SortExpr{Input: input} 201 } 202 203 // rootSentinel is used as the root value when Root is used. 204 type rootSentinel struct { 205 expr memo.RelExpr 206 required *physical.Required 207 } 208 209 // Presentation converts a ColList to a Presentation. 210 func (c *customFuncs) Presentation(cols opt.ColList) physical.Presentation { 211 res := make(physical.Presentation, len(cols)) 212 for i := range cols { 213 res[i].ID = cols[i] 214 res[i].Alias = c.mem.Metadata().ColumnMeta(cols[i]).Alias 215 } 216 return res 217 } 218 219 // NoOrdering returns the empty OrderingChoice. 220 func (c *customFuncs) NoOrdering() physical.OrderingChoice { 221 return physical.OrderingChoice{} 222 } 223 224 // Root can be used only at the top level on an expression, to annotate the 225 // root with a presentation and/or required ordering. The operator must be able 226 // to provide the ordering. For example: 227 // (Root 228 // ( ... ) 229 // (Presentation "a,b") 230 // (OrderingChoice "+a") 231 // ) 232 func (c *customFuncs) Root( 233 root memo.RelExpr, presentation physical.Presentation, ordering physical.OrderingChoice, 234 ) *rootSentinel { 235 props := &physical.Required{ 236 Presentation: presentation, 237 Ordering: ordering, 238 } 239 return &rootSentinel{expr: root, required: props} 240 }