github.com/dolthub/go-mysql-server@v0.18.0/sql/analyzer/resolve_columns.go (about) 1 // Copyright 2020-2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package analyzer 16 17 import ( 18 "fmt" 19 "strings" 20 21 "gopkg.in/src-d/go-errors.v1" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/expression" 25 "github.com/dolthub/go-mysql-server/sql/plan" 26 "github.com/dolthub/go-mysql-server/sql/transform" 27 ) 28 29 type tableCol struct { 30 table string 31 col string 32 } 33 34 func newTableCol(table, col string) tableCol { 35 return tableCol{ 36 table: strings.ToLower(table), 37 col: strings.ToLower(col), 38 } 39 } 40 41 var _ sql.Tableable = tableCol{} 42 var _ sql.Nameable = tableCol{} 43 44 func (tc tableCol) Table() string { 45 return tc.table 46 } 47 48 func (tc tableCol) Name() string { 49 return tc.col 50 } 51 52 func (tc tableCol) String() string { 53 if tc.table != "" { 54 return fmt.Sprintf("%s.%s", tc.table, tc.col) 55 } else { 56 return tc.col 57 } 58 } 59 60 type indexedCol struct { 61 *sql.Column 62 index int 63 } 64 65 // column is the common interface that groups UnresolvedColumn and deferredColumn. 66 type column interface { 67 sql.Nameable 68 sql.Tableable 69 sql.Expression 70 } 71 72 var errGlobalVariablesNotSupported = errors.NewKind("can't resolve global variable, %s was requested") 73 74 // indexColumns returns a map of column identifiers to their index in the node's schema. Columns from outer scopes are 75 // included as well, with lower indexes (prepended to node schema) but lower precedence (overwritten by inner nodes in 76 // map) 77 func indexColumns(_ *sql.Context, _ *Analyzer, n sql.Node, scope *plan.Scope) (map[tableCol]indexedCol, error) { 78 var columns = make(map[tableCol]indexedCol) 79 var idx int 80 81 indexColumn := func(col *sql.Column) { 82 columns[tableCol{ 83 table: strings.ToLower(col.Source), 84 col: strings.ToLower(col.Name), 85 }] = indexedCol{col, idx} 86 idx++ 87 } 88 89 indexSchema := func(n sql.Schema) { 90 for _, col := range n { 91 indexColumn(col) 92 } 93 } 94 95 var indexColumnExpr func(e sql.Expression) 96 indexColumnExpr = func(e sql.Expression) { 97 switch e := e.(type) { 98 case *expression.Alias: 99 // Aliases get indexed twice with the same index number: once with the aliased name and once with the 100 // underlying name 101 indexColumn(transform.ExpressionToColumn(e, plan.AliasSubqueryString(e))) 102 idx-- 103 indexColumnExpr(e.Child) 104 default: 105 indexColumn(transform.ExpressionToColumn(e, plan.AliasSubqueryString(e))) 106 } 107 } 108 109 indexChildNode := func(n sql.Node) { 110 switch n := n.(type) { 111 case sql.Projector: 112 for _, e := range n.ProjectedExprs() { 113 indexColumnExpr(e) 114 } 115 case *plan.Values: 116 // values nodes don't have a schema to index like other nodes that provide columns 117 default: 118 indexSchema(n.Schema()) 119 } 120 } 121 122 if scope.OuterRelUnresolved() { 123 // the columns in this relation will be mis-indexed, skip 124 // until outer rel is resolved 125 return nil, nil 126 } 127 128 // Index the columns in the outer scope, outer to inner. This means inner scope columns will overwrite the outer 129 // ones of the same name. This matches the MySQL scope precedence rules. 130 indexSchema(scope.Schema()) 131 132 // For the innermost scope (the node being evaluated), look at the schemas of the children instead of this node 133 // itself. Skip this for DDL nodes that handle indexing separately. 134 shouldIndexChildNode := true 135 switch n.(type) { 136 case *plan.AddColumn, *plan.ModifyColumn: 137 shouldIndexChildNode = false 138 case *plan.RecursiveCte, *plan.SetOp: 139 shouldIndexChildNode = false 140 } 141 142 if shouldIndexChildNode { 143 for _, child := range n.Children() { 144 indexChildNode(child) 145 } 146 } 147 148 // For certain DDL nodes, we have to do more work 149 indexSchemaForDefaults := func(column *sql.Column, order *sql.ColumnOrder, sch sql.Schema) { 150 tblSch := make(sql.Schema, len(sch)) 151 copy(tblSch, sch) 152 if order == nil { 153 tblSch = append(tblSch, column) 154 } else if order.First { 155 tblSch = append(sql.Schema{column}, tblSch...) 156 } else { // must be After 157 index := 1 158 afterColumn := strings.ToLower(order.AfterColumn) 159 for _, col := range tblSch { 160 if strings.ToLower(col.Name) == afterColumn { 161 break 162 } 163 index++ 164 } 165 if index <= len(tblSch) { 166 tblSch = append(tblSch, nil) 167 copy(tblSch[index+1:], tblSch[index:]) 168 tblSch[index] = column 169 } 170 } 171 for _, col := range tblSch { 172 columns[tableCol{ 173 table: "", 174 col: strings.ToLower(col.Name), 175 }] = indexedCol{col, idx} 176 columns[tableCol{ 177 table: strings.ToLower(col.Source), 178 col: strings.ToLower(col.Name), 179 }] = indexedCol{col, idx} 180 idx++ 181 } 182 } 183 184 switch node := n.(type) { 185 case *plan.CreateTable: // For this node in particular, the columns will only come into existence after the analyzer step, so we forge them here. 186 for _, col := range node.CreateSchema.Schema { 187 columns[tableCol{ 188 table: "", 189 col: strings.ToLower(col.Name), 190 }] = indexedCol{col, idx} 191 columns[tableCol{ 192 table: strings.ToLower(col.Source), 193 col: strings.ToLower(col.Name), 194 }] = indexedCol{col, idx} 195 idx++ 196 } 197 case *plan.AddColumn: // Add/Modify need to have the full column set in order to resolve a default expression. 198 tbl := node.Table 199 indexSchemaForDefaults(node.Column(), node.Order(), tbl.Schema()) 200 case *plan.ModifyColumn: 201 tbl := node.Table 202 indexSchemaForDefaults(node.NewColumn(), node.Order(), tbl.Schema()) 203 case *plan.RecursiveCte, *plan.SetOp: 204 // opaque nodes have derived schemas 205 // TODO also subquery aliases? 206 indexChildNode(node.(sql.BinaryNode).Left()) 207 case *plan.InsertInto: 208 // should index columns in InsertInto.Source 209 aliasedTables := make(map[sql.Node]bool) 210 transform.Inspect(node.Source, func(n sql.Node) bool { 211 // need to reset idx for each table found, as this function assumes only 1 table 212 if tblAlias, ok := n.(*plan.TableAlias); ok && tblAlias.Resolved() { 213 idx = 0 214 indexSchema(tblAlias.Schema()) 215 aliasedTables[tblAlias.Child] = true 216 } 217 return true 218 }) 219 transform.Inspect(node.Source, func(n sql.Node) bool { 220 if resTbl, ok := n.(*plan.ResolvedTable); ok && !aliasedTables[resTbl] { 221 indexSchema(resTbl.Schema()) 222 } 223 return true 224 }) 225 transform.Inspect(node.Source, func(n sql.Node) bool { 226 if resTbl, ok := n.(*plan.SubqueryAlias); ok && resTbl.Resolved() && !aliasedTables[resTbl] { 227 idx = 0 228 indexSchema(resTbl.Schema()) 229 } 230 return true 231 }) 232 } 233 234 return columns, nil 235 }