github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/select_funcs.go (about) 1 // Copyright 2020 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 norm 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/sql/opt" 15 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 16 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 17 "github.com/cockroachdb/cockroach/pkg/util" 18 "github.com/cockroachdb/errors" 19 ) 20 21 // CanMapOnSetOp determines whether the filter can be mapped to either 22 // side of a set operator. 23 func (c *CustomFuncs) CanMapOnSetOp(src *memo.FiltersItem) bool { 24 filterProps := src.ScalarProps() 25 for i, ok := filterProps.OuterCols.Next(0); ok; i, ok = filterProps.OuterCols.Next(i + 1) { 26 colType := c.f.Metadata().ColumnMeta(i).Type 27 if sqlbase.HasCompositeKeyEncoding(colType) { 28 return false 29 } 30 } 31 return !filterProps.HasCorrelatedSubquery 32 } 33 34 // MapSetOpFilterLeft maps the filter onto the left expression by replacing 35 // the out columns of the filter with the appropriate corresponding columns in 36 // the left side of the operator. 37 // Useful for pushing filters to relations the set operation is composed of. 38 func (c *CustomFuncs) MapSetOpFilterLeft( 39 filter *memo.FiltersItem, set *memo.SetPrivate, 40 ) opt.ScalarExpr { 41 colMap := makeMapFromColLists(set.OutCols, set.LeftCols) 42 return c.MapFiltersItemCols(filter, colMap) 43 } 44 45 // MapSetOpFilterRight maps the filter onto the right expression by replacing 46 // the out columns of the filter with the appropriate corresponding columns in 47 // the right side of the operator. 48 // Useful for pushing filters to relations the set operation is composed of. 49 func (c *CustomFuncs) MapSetOpFilterRight( 50 filter *memo.FiltersItem, set *memo.SetPrivate, 51 ) opt.ScalarExpr { 52 colMap := makeMapFromColLists(set.OutCols, set.RightCols) 53 return c.MapFiltersItemCols(filter, colMap) 54 } 55 56 // makeMapFromColLists maps each column ID in src to a column ID in dst. The 57 // columns IDs are mapped based on their relative positions in the column lists, 58 // e.g. the third item in src maps to the third item in dst. The lists must be 59 // of equal length. 60 func makeMapFromColLists(src opt.ColList, dst opt.ColList) util.FastIntMap { 61 if len(src) != len(dst) { 62 panic(errors.AssertionFailedf("src and dst must have the same length, src: %v, dst: %v", src, dst)) 63 } 64 65 var colMap util.FastIntMap 66 for colIndex, outColID := range src { 67 colMap.Set(int(outColID), int(dst[colIndex])) 68 } 69 return colMap 70 } 71 72 // MapFiltersItemCols maps filter expressions by replacing occurrences of 73 // the keys of colMap with the corresponding values. Outer columns are not 74 // replaced. 75 func (c *CustomFuncs) MapFiltersItemCols( 76 filter *memo.FiltersItem, colMap util.FastIntMap, 77 ) opt.ScalarExpr { 78 // Recursively walk the scalar sub-tree looking for references to columns 79 // that need to be replaced and then replace them appropriately. 80 var replace ReplaceFunc 81 replace = func(nd opt.Expr) opt.Expr { 82 switch t := nd.(type) { 83 case *memo.VariableExpr: 84 dstCol, ok := colMap.Get(int(t.Col)) 85 if !ok { 86 // It is not part of the output cols so no replacement required. 87 return nd 88 } 89 return c.f.ConstructVariable(opt.ColumnID(dstCol)) 90 } 91 return c.f.Replace(nd, replace) 92 } 93 94 return replace(filter.Condition).(opt.ScalarExpr) 95 } 96 97 // GroupingAndConstCols returns the grouping columns and ConstAgg columns (for 98 // which the input and output column IDs match). A filter on these columns can 99 // be pushed through a GroupBy. 100 func (c *CustomFuncs) GroupingAndConstCols( 101 grouping *memo.GroupingPrivate, aggs memo.AggregationsExpr, 102 ) opt.ColSet { 103 result := grouping.GroupingCols.Copy() 104 105 // Add any ConstAgg columns. 106 for i := range aggs { 107 item := &aggs[i] 108 if constAgg, ok := item.Agg.(*memo.ConstAggExpr); ok { 109 // Verify that the input and output column IDs match. 110 if item.Col == constAgg.Input.(*memo.VariableExpr).Col { 111 result.Add(item.Col) 112 } 113 } 114 } 115 return result 116 } 117 118 // CanConsolidateFilters returns true if there are at least two different 119 // filter conditions that contain the same variable, where the conditions 120 // have tight constraints and contain a single variable. For example, 121 // CanConsolidateFilters returns true with filters {x > 5, x < 10}, but false 122 // with {x > 5, y < 10} and {x > 5, x = y}. 123 func (c *CustomFuncs) CanConsolidateFilters(filters memo.FiltersExpr) bool { 124 var seen opt.ColSet 125 for i := range filters { 126 if col, ok := c.canConsolidateFilter(&filters[i]); ok { 127 if seen.Contains(col) { 128 return true 129 } 130 seen.Add(col) 131 } 132 } 133 return false 134 } 135 136 // canConsolidateFilter determines whether a filter condition can be 137 // consolidated. Filters can be consolidated if they have tight constraints 138 // and contain a single variable. Examples of such filters include x < 5 and 139 // x IS NULL. If the filter can be consolidated, canConsolidateFilter returns 140 // the column ID of the variable and ok=true. Otherwise, canConsolidateFilter 141 // returns ok=false. 142 func (c *CustomFuncs) canConsolidateFilter(filter *memo.FiltersItem) (col opt.ColumnID, ok bool) { 143 if !filter.ScalarProps().TightConstraints { 144 return 0, false 145 } 146 147 outerCols := c.OuterCols(filter) 148 if outerCols.Len() != 1 { 149 return 0, false 150 } 151 152 col, _ = outerCols.Next(0) 153 return col, true 154 } 155 156 // ConsolidateFilters consolidates filter conditions that contain the same 157 // variable, where the conditions have tight constraints and contain a single 158 // variable. The consolidated filters are combined with a tree of nested 159 // And operations, and wrapped with a Range expression. 160 // 161 // See the ConsolidateSelectFilters rule for more details about why this is 162 // necessary. 163 func (c *CustomFuncs) ConsolidateFilters(filters memo.FiltersExpr) memo.FiltersExpr { 164 // First find the columns that have filter conditions that can be 165 // consolidated. 166 var seen, seenTwice opt.ColSet 167 for i := range filters { 168 if col, ok := c.canConsolidateFilter(&filters[i]); ok { 169 if seen.Contains(col) { 170 seenTwice.Add(col) 171 } else { 172 seen.Add(col) 173 } 174 } 175 } 176 177 newFilters := make(memo.FiltersExpr, seenTwice.Len(), len(filters)-seenTwice.Len()) 178 179 // newFilters contains an empty item for each of the new Range expressions 180 // that will be created below. Fill in rangeMap to track which column 181 // corresponds to each item. 182 var rangeMap util.FastIntMap 183 i := 0 184 for col, ok := seenTwice.Next(0); ok; col, ok = seenTwice.Next(col + 1) { 185 rangeMap.Set(int(col), i) 186 i++ 187 } 188 189 // Iterate through each existing filter condition, and either consolidate it 190 // into one of the new Range expressions or add it unchanged to the new 191 // filters. 192 for i := range filters { 193 if col, ok := c.canConsolidateFilter(&filters[i]); ok && seenTwice.Contains(col) { 194 // This is one of the filter conditions that can be consolidated into a 195 // Range. 196 cond := filters[i].Condition 197 switch t := cond.(type) { 198 case *memo.RangeExpr: 199 // If it is already a range expression, unwrap it. 200 cond = t.And 201 } 202 rangeIdx, _ := rangeMap.Get(int(col)) 203 rangeItem := &newFilters[rangeIdx] 204 if rangeItem.Condition == nil { 205 // This is the first condition. 206 rangeItem.Condition = cond 207 } else { 208 // Build a left-deep tree of ANDs sorted by ID. 209 rangeItem.Condition = c.mergeSortedAnds(rangeItem.Condition, cond) 210 } 211 } else { 212 newFilters = append(newFilters, filters[i]) 213 } 214 } 215 216 // Construct each of the new Range operators now that we have built the 217 // conjunctions. 218 for i, n := 0, seenTwice.Len(); i < n; i++ { 219 newFilters[i] = c.f.ConstructFiltersItem(c.f.ConstructRange(newFilters[i].Condition)) 220 } 221 222 return newFilters 223 } 224 225 // mergeSortedAnds merges two left-deep trees of nested AndExprs sorted by ID. 226 // Returns a single sorted, left-deep tree of nested AndExprs, with any 227 // duplicate expressions eliminated. 228 func (c *CustomFuncs) mergeSortedAnds(left, right opt.ScalarExpr) opt.ScalarExpr { 229 if right == nil { 230 return left 231 } 232 if left == nil { 233 return right 234 } 235 236 // Since both trees are left-deep, perform a merge-sort from right to left. 237 nextLeft := left 238 nextRight := right 239 var remainingLeft, remainingRight opt.ScalarExpr 240 if and, ok := left.(*memo.AndExpr); ok { 241 remainingLeft = and.Left 242 nextLeft = and.Right 243 } 244 if and, ok := right.(*memo.AndExpr); ok { 245 remainingRight = and.Left 246 nextRight = and.Right 247 } 248 249 if nextLeft.ID() == nextRight.ID() { 250 // Eliminate duplicates. 251 return c.mergeSortedAnds(left, remainingRight) 252 } 253 if nextLeft.ID() < nextRight.ID() { 254 return c.f.ConstructAnd(c.mergeSortedAnds(left, remainingRight), nextRight) 255 } 256 return c.f.ConstructAnd(c.mergeSortedAnds(remainingLeft, right), nextLeft) 257 } 258 259 // AreFiltersSorted determines whether the expressions in a FiltersExpr are 260 // ordered by their expression IDs. 261 func (c *CustomFuncs) AreFiltersSorted(f memo.FiltersExpr) bool { 262 for i, n := 0, f.ChildCount(); i < n-1; i++ { 263 if f.Child(i).Child(0).(opt.ScalarExpr).ID() > f.Child(i+1).Child(0).(opt.ScalarExpr).ID() { 264 return false 265 } 266 } 267 return true 268 } 269 270 // SortFilters sorts a filter list by the IDs of the expressions. This has the 271 // effect of canonicalizing FiltersExprs which may have the same filters, but 272 // in a different order. 273 func (c *CustomFuncs) SortFilters(f memo.FiltersExpr) memo.FiltersExpr { 274 result := make(memo.FiltersExpr, len(f)) 275 for i, n := 0, f.ChildCount(); i < n; i++ { 276 fi := f.Child(i).(*memo.FiltersItem) 277 result[i] = *fi 278 } 279 result.Sort() 280 return result 281 } 282 283 // SimplifyFilters removes True operands from a FiltersExpr, and normalizes any 284 // False or Null condition to a single False condition. Null values map to False 285 // because FiltersExpr are only used by Select and Join, both of which treat a 286 // Null filter conjunct exactly as if it were false. 287 // 288 // SimplifyFilters also "flattens" any And operator child by merging its 289 // conditions into a new FiltersExpr list. If, after simplification, no operands 290 // remain, then SimplifyFilters returns an empty FiltersExpr. 291 // 292 // This method assumes that the NormalizeNestedAnds rule has already run and 293 // ensured a left deep And tree. If not (maybe because it's a testing scenario), 294 // then this rule may rematch, but it should still make forward progress). 295 func (c *CustomFuncs) SimplifyFilters(filters memo.FiltersExpr) memo.FiltersExpr { 296 // Start by counting the number of conjuncts that will be flattened so that 297 // the capacity of the FiltersExpr list can be determined. 298 cnt := 0 299 for _, item := range filters { 300 cnt++ 301 condition := item.Condition 302 for condition.Op() == opt.AndOp { 303 cnt++ 304 condition = condition.(*memo.AndExpr).Left 305 } 306 } 307 308 // Construct new filter list. 309 newFilters := make(memo.FiltersExpr, 0, cnt) 310 for _, item := range filters { 311 var ok bool 312 if newFilters, ok = c.addConjuncts(item.Condition, newFilters); !ok { 313 return memo.FiltersExpr{c.f.ConstructFiltersItem(memo.FalseSingleton)} 314 } 315 } 316 317 return newFilters 318 } 319 320 // IsUnsimplifiableOr returns true if this is an OR where neither side is 321 // NULL. SimplifyFilters simplifies ORs with a NULL on one side to its other 322 // side. However other ORs don't simplify. This function is used to prevent 323 // infinite recursion during ConstructFilterItem in SimplifyFilters. This 324 // function must be kept in sync with SimplifyFilters. 325 func (c *CustomFuncs) IsUnsimplifiableOr(item *memo.FiltersItem) bool { 326 or, ok := item.Condition.(*memo.OrExpr) 327 if !ok { 328 return false 329 } 330 return or.Left.Op() != opt.NullOp && or.Right.Op() != opt.NullOp 331 } 332 333 // addConjuncts recursively walks a scalar expression as long as it continues to 334 // find nested And operators. It adds any conjuncts (ignoring True operators) to 335 // the given FiltersExpr and returns true. If it finds a False or Null operator, 336 // it propagates a false return value all the up the call stack, and 337 // SimplifyFilters maps that to a FiltersExpr that is always false. 338 func (c *CustomFuncs) addConjuncts( 339 scalar opt.ScalarExpr, filters memo.FiltersExpr, 340 ) (_ memo.FiltersExpr, ok bool) { 341 switch t := scalar.(type) { 342 case *memo.AndExpr: 343 var ok bool 344 if filters, ok = c.addConjuncts(t.Left, filters); !ok { 345 return nil, false 346 } 347 return c.addConjuncts(t.Right, filters) 348 349 case *memo.FalseExpr, *memo.NullExpr: 350 // Filters expression evaluates to False if any operand is False or Null. 351 return nil, false 352 353 case *memo.TrueExpr: 354 // Filters operator skips True operands. 355 356 case *memo.OrExpr: 357 // If NULL is on either side, take the other side. 358 if t.Left.Op() == opt.NullOp { 359 filters = append(filters, c.f.ConstructFiltersItem(t.Right)) 360 } else if t.Right.Op() == opt.NullOp { 361 filters = append(filters, c.f.ConstructFiltersItem(t.Left)) 362 } else { 363 filters = append(filters, c.f.ConstructFiltersItem(t)) 364 } 365 366 default: 367 filters = append(filters, c.f.ConstructFiltersItem(t)) 368 } 369 return filters, true 370 }