github.com/dolthub/go-mysql-server@v0.18.0/sql/analyzer/filters.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 "reflect" 19 "strings" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/expression" 23 "github.com/dolthub/go-mysql-server/sql/plan" 24 "github.com/dolthub/go-mysql-server/sql/transform" 25 ) 26 27 type filtersByTable map[string][]sql.Expression 28 29 func newFiltersByTable() filtersByTable { 30 return make(filtersByTable) 31 } 32 33 func (f filtersByTable) merge(f2 filtersByTable) { 34 for k, exprs := range f2 { 35 f[k] = append(f[k], exprs...) 36 } 37 } 38 39 func (f filtersByTable) size() int { 40 return len(f) 41 } 42 43 // getFiltersByTable returns a map of table name to filter expressions on that table for the node provided. Any 44 // predicates that contain no table or more than one table are not included in the result. 45 func getFiltersByTable(n sql.Node) filtersByTable { 46 filters := newFiltersByTable() 47 transform.Inspect(n, func(node sql.Node) bool { 48 switch node := node.(type) { 49 case *plan.Filter: 50 fs := exprToTableFilters(node.Expression) 51 filters.merge(fs) 52 } 53 if o, ok := node.(sql.OpaqueNode); ok { 54 return !o.Opaque() 55 } 56 return true 57 }) 58 59 return filters 60 } 61 62 // exprToTableFilters returns a map of table name to filter expressions on that table for all parts of the expression 63 // given, split at AND. Any expressions that contain subquerys, or refer to more than one table, are not included in 64 // the result. 65 func exprToTableFilters(expr sql.Expression) filtersByTable { 66 filters := newFiltersByTable() 67 for _, expr := range expression.SplitConjunction(expr) { 68 var seenTables = make(map[string]bool) 69 var lastTable string 70 hasSubquery := false 71 sql.Inspect(expr, func(e sql.Expression) bool { 72 f, ok := e.(*expression.GetField) 73 if ok { 74 if !seenTables[f.Table()] { 75 seenTables[f.Table()] = true 76 lastTable = f.Table() 77 } 78 } else if _, isSubquery := e.(*plan.Subquery); isSubquery { 79 hasSubquery = true 80 return false 81 } 82 83 return true 84 }) 85 86 if len(seenTables) == 1 && !hasSubquery { 87 filters[lastTable] = append(filters[lastTable], expr) 88 } 89 } 90 91 return filters 92 } 93 94 type filterSet struct { 95 filterPredicates []sql.Expression 96 filtersByTable filtersByTable 97 handledFilters []sql.Expression 98 handledIndexFilters []string 99 tableAliases TableAliases 100 } 101 102 // newFilterSet returns a new filter set that will track available filters with the filters and aliases given. Aliases 103 // are necessary to normalize expressions from indexes when in the presence of aliases. 104 func newFilterSet(filter sql.Expression, filtersByTable filtersByTable, tableAliases TableAliases) *filterSet { 105 return &filterSet{ 106 filterPredicates: expression.SplitConjunction(filter), 107 filtersByTable: filtersByTable, 108 tableAliases: tableAliases, 109 } 110 } 111 112 // availableFiltersForTable returns the filters that are still available for the table given (not previously marked 113 // handled) 114 func (fs *filterSet) availableFiltersForTable(ctx *sql.Context, table string) []sql.Expression { 115 filters, ok := fs.filtersByTable[strings.ToLower(table)] 116 if !ok { 117 return nil 118 } 119 return fs.subtractUsedIndexes(ctx, subtractExprSet(filters, fs.handledFilters)) 120 } 121 122 // unhandledPredicates returns the filters that are still available (not previously marked handled) 123 func (fs *filterSet) unhandledPredicates(ctx *sql.Context) []sql.Expression { 124 var available []sql.Expression 125 for _, e := range fs.filterPredicates { 126 available = append(available, fs.subtractUsedIndexes(ctx, subtractExprSet([]sql.Expression{e}, fs.handledFilters))...) 127 } 128 return available 129 } 130 131 // handledCount returns the number of filter expressions that have been marked as handled 132 func (fs *filterSet) handledCount() int { 133 return len(fs.handledIndexFilters) + len(fs.handledFilters) 134 } 135 136 // markFilterUsed marks the filter given as handled, so it will no longer be returned by availableFiltersForTable 137 func (fs *filterSet) markFiltersHandled(exprs ...sql.Expression) { 138 fs.handledFilters = append(fs.handledFilters, exprs...) 139 } 140 141 // markIndexesHandled marks the indexes given as handled, so expressions on them will no longer be returned by 142 // availableFiltersForTable 143 // TODO: this is currently unused because we can't safely remove indexed predicates from the filter in all cases 144 func (fs *filterSet) markIndexesHandled(indexes []sql.Index) { 145 for _, index := range indexes { 146 fs.handledIndexFilters = append(fs.handledIndexFilters, index.Expressions()...) 147 } 148 } 149 150 // subtractExprSet returns all expressions in the first parameter that aren't present in the second. 151 func subtractExprSet(all, toSubtract []sql.Expression) []sql.Expression { 152 var remainder []sql.Expression 153 154 for _, e := range all { 155 var found bool 156 for _, s := range toSubtract { 157 if reflect.DeepEqual(e, s) { 158 found = true 159 break 160 } 161 } 162 163 if !found { 164 remainder = append(remainder, e) 165 } 166 } 167 168 return remainder 169 } 170 171 // subtractUsedIndexes returns the filter expressions given with used indexes subtracted off. 172 func (fs *filterSet) subtractUsedIndexes(ctx *sql.Context, all []sql.Expression) []sql.Expression { 173 var remainder []sql.Expression 174 175 // Careful: index expressions are always normalized (contain actual table names), whereas filter expressions can 176 // contain aliases for both expressions and table names. We want to normalize all expressions for comparison, but 177 // return the original expressions. 178 normalized := normalizeExpressions(fs.tableAliases, all...) 179 180 for i, e := range normalized { 181 var found bool 182 183 cmpStr := e.String() 184 comparable, ok := e.(expression.Comparer) 185 if ok { 186 left, right := comparable.Left(), comparable.Right() 187 if _, ok := left.(*expression.GetField); ok { 188 cmpStr = left.String() 189 } else { 190 cmpStr = right.String() 191 } 192 } 193 194 for _, s := range fs.handledIndexFilters { 195 if cmpStr == s { 196 found = true 197 break 198 } 199 } 200 201 if !found { 202 remainder = append(remainder, all[i]) 203 } 204 } 205 206 return remainder 207 }