github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/prune_cols_funcs.go (about) 1 // Copyright 2018 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/cat" 16 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/props" 18 "github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical" 19 "github.com/cockroachdb/cockroach/pkg/sql/types" 20 ) 21 22 // NeededGroupingCols returns the columns needed by a grouping operator's 23 // grouping columns or requested ordering. 24 func (c *CustomFuncs) NeededGroupingCols(private *memo.GroupingPrivate) opt.ColSet { 25 return private.GroupingCols.Union(private.Ordering.ColSet()) 26 } 27 28 // NeededOrdinalityCols returns the columns needed by a Ordinality operator's 29 // requested ordering. 30 func (c *CustomFuncs) NeededOrdinalityCols(private *memo.OrdinalityPrivate) opt.ColSet { 31 return private.Ordering.ColSet() 32 } 33 34 // NeededExplainCols returns the columns needed by Explain's required physical 35 // properties. 36 func (c *CustomFuncs) NeededExplainCols(private *memo.ExplainPrivate) opt.ColSet { 37 return private.Props.ColSet() 38 } 39 40 // NeededMutationCols returns the columns needed by a mutation operator. Note 41 // that this function makes no attempt to determine the minimal set of columns 42 // needed by the mutation private; it simply returns the input columns that are 43 // referenced by it. Other rules filter the FetchCols, CheckCols, etc. and can 44 // in turn trigger the PruneMutationInputCols rule. 45 func (c *CustomFuncs) NeededMutationCols( 46 private *memo.MutationPrivate, checks memo.FKChecksExpr, 47 ) opt.ColSet { 48 var cols opt.ColSet 49 50 // Add all input columns referenced by the mutation private. 51 addCols := func(list opt.ColList) { 52 for _, id := range list { 53 if id != 0 { 54 cols.Add(id) 55 } 56 } 57 } 58 59 addCols(private.InsertCols) 60 addCols(private.FetchCols) 61 addCols(private.UpdateCols) 62 addCols(private.CheckCols) 63 addCols(private.IndexPredicateCols) 64 addCols(private.ReturnCols) 65 addCols(private.PassthroughCols) 66 if private.CanaryCol != 0 { 67 cols.Add(private.CanaryCol) 68 } 69 70 if private.WithID != 0 { 71 for i := range checks { 72 withUses := c.WithUses(checks[i].Check) 73 cols.UnionWith(withUses[private.WithID].UsedCols) 74 } 75 } 76 77 return cols 78 } 79 80 // NeededMutationFetchCols returns the set of FetchCols needed by the given 81 // mutation operator. FetchCols are existing values that are fetched from the 82 // store and are then used to construct keys and values for update or delete KV 83 // operations. 84 func (c *CustomFuncs) NeededMutationFetchCols( 85 op opt.Operator, private *memo.MutationPrivate, 86 ) opt.ColSet { 87 return neededMutationFetchCols(c.mem, op, private) 88 } 89 90 // neededMutationFetchCols returns the set of columns needed by the given 91 // mutation operator. 92 func neededMutationFetchCols( 93 mem *memo.Memo, op opt.Operator, private *memo.MutationPrivate, 94 ) opt.ColSet { 95 96 var cols opt.ColSet 97 tabMeta := mem.Metadata().TableMeta(private.Table) 98 99 // familyCols returns the columns in the given family. 100 familyCols := func(fam cat.Family) opt.ColSet { 101 var colSet opt.ColSet 102 for i, n := 0, fam.ColumnCount(); i < n; i++ { 103 id := tabMeta.MetaID.ColumnID(fam.Column(i).Ordinal) 104 colSet.Add(id) 105 } 106 return colSet 107 } 108 109 // addFamilyCols adds all columns in each family containing at least one 110 // column that is being updated. 111 addFamilyCols := func(updateCols opt.ColSet) { 112 for i, n := 0, tabMeta.Table.FamilyCount(); i < n; i++ { 113 famCols := familyCols(tabMeta.Table.Family(i)) 114 if famCols.Intersects(updateCols) { 115 cols.UnionWith(famCols) 116 } 117 } 118 } 119 120 // Retain any FetchCols that are needed for ReturnCols. If a RETURN column 121 // is needed, then: 122 // 1. For Delete, the corresponding FETCH column is always needed, since 123 // it is always returned. 124 // 2. For Update, the corresponding FETCH column is needed when there is 125 // no corresponding UPDATE column. In that case, the FETCH column always 126 // becomes the RETURN column. 127 // 3. For Upsert, the corresponding FETCH column is needed when there is 128 // no corresponding UPDATE column. In that case, either the INSERT or 129 // FETCH column becomes the RETURN column, so both must be available 130 // for the CASE expression. 131 for ord, col := range private.ReturnCols { 132 if col != 0 { 133 if op == opt.DeleteOp || len(private.UpdateCols) == 0 || private.UpdateCols[ord] == 0 { 134 cols.Add(tabMeta.MetaID.ColumnID(ord)) 135 } 136 } 137 } 138 139 switch op { 140 case opt.UpdateOp, opt.UpsertOp: 141 // Determine set of target table columns that need to be updated. 142 var updateCols opt.ColSet 143 for ord, col := range private.UpdateCols { 144 if col != 0 { 145 updateCols.Add(tabMeta.MetaID.ColumnID(ord)) 146 } 147 } 148 149 // Make sure to consider indexes that are being added or dropped. 150 for i, n := 0, tabMeta.Table.DeletableIndexCount(); i < n; i++ { 151 indexCols := tabMeta.IndexColumns(i) 152 if !indexCols.Intersects(updateCols) { 153 // This index is not being updated. 154 continue 155 } 156 157 // Always add index strict key columns, since these are needed to fetch 158 // existing rows from the store. 159 keyCols := tabMeta.IndexKeyColumns(i) 160 cols.UnionWith(keyCols) 161 162 // Add all columns in any family that includes an update column. 163 // It is possible to update a subset of families only for the primary 164 // index, and only when key columns are not being updated. Otherwise, 165 // all columns in the index must be fetched. 166 // TODO(andyk): It should be possible to not include columns that are 167 // being updated, since the existing value is not used. However, this 168 // would require execution support. 169 if i == cat.PrimaryIndex && !keyCols.Intersects(updateCols) { 170 addFamilyCols(updateCols) 171 } else { 172 cols.UnionWith(indexCols) 173 } 174 } 175 176 case opt.DeleteOp: 177 // Add in all strict key columns from all indexes, since these are needed 178 // to compose the keys of rows to delete. Include mutation indexes, since 179 // it is necessary to delete rows even from indexes that are being added 180 // or dropped. 181 for i, n := 0, tabMeta.Table.DeletableIndexCount(); i < n; i++ { 182 cols.UnionWith(tabMeta.IndexKeyColumns(i)) 183 } 184 } 185 186 return cols 187 } 188 189 // CanPruneCols returns true if the target expression has extra columns that are 190 // not needed at this level of the tree, and can be eliminated by one of the 191 // PruneCols rules. CanPruneCols uses the PruneCols property to determine the 192 // set of columns which can be pruned, and subtracts the given set of additional 193 // needed columns from that. See the props.Relational.Rule.PruneCols comment for 194 // more details. 195 func (c *CustomFuncs) CanPruneCols(target memo.RelExpr, neededCols opt.ColSet) bool { 196 return !DerivePruneCols(target).SubsetOf(neededCols) 197 } 198 199 // CanPruneAggCols returns true if one or more of the target aggregations is not 200 // referenced and can be eliminated. 201 func (c *CustomFuncs) CanPruneAggCols(target memo.AggregationsExpr, neededCols opt.ColSet) bool { 202 return !target.OutputCols().SubsetOf(neededCols) 203 } 204 205 // CanPruneMutationFetchCols returns true if there are any FetchCols that are 206 // not in the set of needed columns. Those extra FetchCols can be pruned. 207 func (c *CustomFuncs) CanPruneMutationFetchCols( 208 private *memo.MutationPrivate, neededCols opt.ColSet, 209 ) bool { 210 tabMeta := c.mem.Metadata().TableMeta(private.Table) 211 for ord, col := range private.FetchCols { 212 if col != 0 && !neededCols.Contains(tabMeta.MetaID.ColumnID(ord)) { 213 return true 214 } 215 } 216 return false 217 } 218 219 // PruneCols creates an expression that discards any outputs columns of the 220 // target expression that are not used. If the target expression type supports 221 // column filtering (like Scan, Values, Projections, etc.), then create a new 222 // instance of that operator that does the filtering. Otherwise, construct a 223 // Project operator that wraps the operator and does the filtering. The new 224 // Project operator will be pushed down the tree until it merges with another 225 // operator that supports column filtering. 226 func (c *CustomFuncs) PruneCols(target memo.RelExpr, neededCols opt.ColSet) memo.RelExpr { 227 switch t := target.(type) { 228 case *memo.ScanExpr: 229 return c.pruneScanCols(t, neededCols) 230 231 case *memo.ValuesExpr: 232 return c.pruneValuesCols(t, neededCols) 233 234 case *memo.WithScanExpr: 235 return c.pruneWithScanCols(t, neededCols) 236 237 case *memo.ProjectExpr: 238 passthrough := t.Passthrough.Intersection(neededCols) 239 projections := make(memo.ProjectionsExpr, 0, len(t.Projections)) 240 for i := range t.Projections { 241 item := &t.Projections[i] 242 if neededCols.Contains(item.Col) { 243 projections = append(projections, *item) 244 } 245 } 246 return c.f.ConstructProject(t.Input, projections, passthrough) 247 248 default: 249 // In other cases, we wrap the input in a Project operator. 250 251 // Get the subset of the target expression's output columns that should 252 // not be pruned. Don't prune if the target output column is needed by a 253 // higher-level expression, or if it's not part of the PruneCols set. 254 pruneCols := DerivePruneCols(target).Difference(neededCols) 255 colSet := c.OutputCols(target).Difference(pruneCols) 256 return c.f.ConstructProject(target, memo.EmptyProjectionsExpr, colSet) 257 } 258 } 259 260 // PruneAggCols creates a new AggregationsExpr that discards columns that are 261 // not referenced by the neededCols set. 262 func (c *CustomFuncs) PruneAggCols( 263 target memo.AggregationsExpr, neededCols opt.ColSet, 264 ) memo.AggregationsExpr { 265 aggs := make(memo.AggregationsExpr, 0, len(target)) 266 for i := range target { 267 item := &target[i] 268 if neededCols.Contains(item.Col) { 269 aggs = append(aggs, *item) 270 } 271 } 272 return aggs 273 } 274 275 // PruneMutationFetchCols rewrites the given mutation private to no longer 276 // reference FetchCols that are not part of the neededCols set. The caller must 277 // have already done the analysis to prove that these columns are not needed, by 278 // calling CanPruneMutationFetchCols. 279 func (c *CustomFuncs) PruneMutationFetchCols( 280 private *memo.MutationPrivate, neededCols opt.ColSet, 281 ) *memo.MutationPrivate { 282 tabID := c.mem.Metadata().TableMeta(private.Table).MetaID 283 newPrivate := *private 284 newPrivate.FetchCols = c.filterMutationList(tabID, newPrivate.FetchCols, neededCols) 285 return &newPrivate 286 } 287 288 // filterMutationList filters the given mutation list by setting any columns 289 // that are not in the neededCols set to zero. This indicates that those input 290 // columns are not needed by this mutation list. 291 func (c *CustomFuncs) filterMutationList( 292 tabID opt.TableID, inList opt.ColList, neededCols opt.ColSet, 293 ) opt.ColList { 294 newList := make(opt.ColList, len(inList)) 295 for i, c := range inList { 296 if !neededCols.Contains(tabID.ColumnID(i)) { 297 newList[i] = 0 298 } else { 299 newList[i] = c 300 } 301 } 302 return newList 303 } 304 305 // pruneScanCols constructs a new Scan operator based on the given existing Scan 306 // operator, but projecting only the needed columns. 307 func (c *CustomFuncs) pruneScanCols(scan *memo.ScanExpr, neededCols opt.ColSet) memo.RelExpr { 308 // Make copy of scan private and update columns. 309 new := scan.ScanPrivate 310 new.Cols = c.OutputCols(scan).Intersection(neededCols) 311 return c.f.ConstructScan(&new) 312 } 313 314 // pruneWithScanCols constructs a new WithScan operator based on the given 315 // existing WithScan operator, but projecting only the needed columns. 316 func (c *CustomFuncs) pruneWithScanCols( 317 scan *memo.WithScanExpr, neededCols opt.ColSet, 318 ) memo.RelExpr { 319 // Make copy of scan private and update columns. 320 new := scan.WithScanPrivate 321 322 new.InCols = make(opt.ColList, 0, neededCols.Len()) 323 new.OutCols = make(opt.ColList, 0, neededCols.Len()) 324 for i := range scan.WithScanPrivate.OutCols { 325 if neededCols.Contains(scan.WithScanPrivate.OutCols[i]) { 326 new.InCols = append(new.InCols, scan.WithScanPrivate.InCols[i]) 327 new.OutCols = append(new.OutCols, scan.WithScanPrivate.OutCols[i]) 328 } 329 } 330 331 return c.f.ConstructWithScan(&new) 332 } 333 334 // pruneValuesCols constructs a new Values operator based on the given existing 335 // Values operator. The new operator will have the same set of rows, but 336 // containing only the needed columns. Other columns are discarded. 337 func (c *CustomFuncs) pruneValuesCols(values *memo.ValuesExpr, neededCols opt.ColSet) memo.RelExpr { 338 // Create new list of columns that only contains needed columns. 339 newCols := make(opt.ColList, 0, neededCols.Len()) 340 for _, colID := range values.Cols { 341 if !neededCols.Contains(colID) { 342 continue 343 } 344 newCols = append(newCols, colID) 345 } 346 347 newRows := make(memo.ScalarListExpr, len(values.Rows)) 348 for irow, row := range values.Rows { 349 tuple := row.(*memo.TupleExpr) 350 typ := tuple.DataType() 351 352 newContents := make([]*types.T, len(newCols)) 353 newElems := make(memo.ScalarListExpr, len(newCols)) 354 nelem := 0 355 for ielem, elem := range tuple.Elems { 356 if !neededCols.Contains(values.Cols[ielem]) { 357 continue 358 } 359 newContents[nelem] = typ.TupleContents()[ielem] 360 newElems[nelem] = elem 361 nelem++ 362 } 363 364 newRows[irow] = c.f.ConstructTuple(newElems, types.MakeTuple(newContents)) 365 } 366 367 return c.f.ConstructValues(newRows, &memo.ValuesPrivate{ 368 Cols: newCols, 369 ID: values.ID, 370 }) 371 } 372 373 // PruneOrderingGroupBy removes any columns referenced by the Ordering inside 374 // a GroupingPrivate which are not part of the neededCols set. 375 func (c *CustomFuncs) PruneOrderingGroupBy( 376 private *memo.GroupingPrivate, neededCols opt.ColSet, 377 ) *memo.GroupingPrivate { 378 if private.Ordering.SubsetOfCols(neededCols) { 379 return private 380 } 381 382 // Make copy of grouping private and update columns. 383 new := *private 384 new.Ordering = new.Ordering.Copy() 385 new.Ordering.ProjectCols(neededCols) 386 return &new 387 } 388 389 // PruneOrderingOrdinality removes any columns referenced by the Ordering inside 390 // a OrdinalityPrivate which are not part of the neededCols set. 391 func (c *CustomFuncs) PruneOrderingOrdinality( 392 private *memo.OrdinalityPrivate, neededCols opt.ColSet, 393 ) *memo.OrdinalityPrivate { 394 if private.Ordering.SubsetOfCols(neededCols) { 395 return private 396 } 397 398 // Make copy of row number private and update columns. 399 new := *private 400 new.Ordering = new.Ordering.Copy() 401 new.Ordering.ProjectCols(neededCols) 402 return &new 403 } 404 405 // NeededWindowCols is the set of columns that the window function needs to 406 // execute. 407 func (c *CustomFuncs) NeededWindowCols(windows memo.WindowsExpr, p *memo.WindowPrivate) opt.ColSet { 408 var needed opt.ColSet 409 needed.UnionWith(p.Partition) 410 needed.UnionWith(p.Ordering.ColSet()) 411 for i := range windows { 412 needed.UnionWith(windows[i].ScalarProps().OuterCols) 413 } 414 return needed 415 } 416 417 // CanPruneWindows is true if the list of window functions contains a column 418 // which is not included in needed, meaning that it can be pruned. 419 func (c *CustomFuncs) CanPruneWindows(needed opt.ColSet, windows memo.WindowsExpr) bool { 420 for _, w := range windows { 421 if !needed.Contains(w.Col) { 422 return true 423 } 424 } 425 return false 426 } 427 428 // PruneWindows restricts windows to only the columns which appear in needed. 429 // If we eliminate all the window functions, EliminateWindow will trigger and 430 // remove the expression entirely. 431 func (c *CustomFuncs) PruneWindows(needed opt.ColSet, windows memo.WindowsExpr) memo.WindowsExpr { 432 result := make(memo.WindowsExpr, 0, len(windows)) 433 for _, w := range windows { 434 if needed.Contains(w.Col) { 435 result = append(result, w) 436 } 437 } 438 return result 439 } 440 441 // DerivePruneCols returns the subset of the given expression's output columns 442 // that are candidates for pruning. Each operator has its own custom rule for 443 // what columns it allows to be pruned. Note that if an operator allows columns 444 // to be pruned, then there must be logic in the PruneCols method to actually 445 // prune those columns when requested. 446 func DerivePruneCols(e memo.RelExpr) opt.ColSet { 447 relProps := e.Relational() 448 if relProps.IsAvailable(props.PruneCols) { 449 return relProps.Rule.PruneCols 450 } 451 relProps.SetAvailable(props.PruneCols) 452 453 switch e.Op() { 454 case opt.ScanOp, opt.ValuesOp, opt.WithScanOp: 455 // All columns can potentially be pruned from the Scan, Values, and WithScan 456 // operators. 457 relProps.Rule.PruneCols = relProps.OutputCols.Copy() 458 459 case opt.SelectOp: 460 // Any pruneable input columns can potentially be pruned, as long as they're 461 // not used by the filter. 462 sel := e.(*memo.SelectExpr) 463 relProps.Rule.PruneCols = DerivePruneCols(sel.Input).Copy() 464 usedCols := sel.Filters.OuterCols(e.Memo()) 465 relProps.Rule.PruneCols.DifferenceWith(usedCols) 466 467 case opt.ProjectOp: 468 // All columns can potentially be pruned from the Project, if they're never 469 // used in a higher-level expression. 470 relProps.Rule.PruneCols = relProps.OutputCols.Copy() 471 472 case opt.InnerJoinOp, opt.LeftJoinOp, opt.RightJoinOp, opt.FullJoinOp, 473 opt.SemiJoinOp, opt.AntiJoinOp, opt.InnerJoinApplyOp, opt.LeftJoinApplyOp, 474 opt.SemiJoinApplyOp, opt.AntiJoinApplyOp: 475 // Any pruneable columns from projected inputs can potentially be pruned, as 476 // long as they're not used by the right input (i.e. in Apply case) or by 477 // the join filter. 478 left := e.Child(0).(memo.RelExpr) 479 leftPruneCols := DerivePruneCols(left) 480 right := e.Child(1).(memo.RelExpr) 481 rightPruneCols := DerivePruneCols(right) 482 483 switch e.Op() { 484 case opt.SemiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinOp, opt.AntiJoinApplyOp: 485 relProps.Rule.PruneCols = leftPruneCols.Copy() 486 487 default: 488 relProps.Rule.PruneCols = leftPruneCols.Union(rightPruneCols) 489 } 490 relProps.Rule.PruneCols.DifferenceWith(right.Relational().OuterCols) 491 onCols := e.Child(2).(*memo.FiltersExpr).OuterCols(e.Memo()) 492 relProps.Rule.PruneCols.DifferenceWith(onCols) 493 494 case opt.GroupByOp, opt.ScalarGroupByOp, opt.DistinctOnOp, opt.EnsureDistinctOnOp: 495 // Grouping columns can't be pruned, because they were used to group rows. 496 // However, aggregation columns can potentially be pruned. 497 groupingColSet := e.Private().(*memo.GroupingPrivate).GroupingCols 498 if groupingColSet.Empty() { 499 relProps.Rule.PruneCols = relProps.OutputCols.Copy() 500 } else { 501 relProps.Rule.PruneCols = relProps.OutputCols.Difference(groupingColSet) 502 } 503 504 case opt.LimitOp, opt.OffsetOp: 505 // Any pruneable input columns can potentially be pruned, as long as 506 // they're not used as an ordering column. 507 inputPruneCols := DerivePruneCols(e.Child(0).(memo.RelExpr)) 508 ordering := e.Private().(*physical.OrderingChoice).ColSet() 509 relProps.Rule.PruneCols = inputPruneCols.Difference(ordering) 510 511 case opt.OrdinalityOp: 512 // Any pruneable input columns can potentially be pruned, as long as 513 // they're not used as an ordering column. The new row number column 514 // cannot be pruned without adding an additional Project operator, so 515 // don't add it to the set. 516 ord := e.(*memo.OrdinalityExpr) 517 inputPruneCols := DerivePruneCols(ord.Input) 518 relProps.Rule.PruneCols = inputPruneCols.Difference(ord.Ordering.ColSet()) 519 520 case opt.IndexJoinOp, opt.LookupJoinOp, opt.MergeJoinOp: 521 // There is no need to prune columns projected by Index, Lookup or Merge 522 // joins, since its parent will always be an "alternate" expression in the 523 // memo. Any pruneable columns should have already been pruned at the time 524 // one of these operators is constructed. Additionally, there is not 525 // currently a PruneCols rule for these operators. 526 527 case opt.ProjectSetOp: 528 // Any pruneable input columns can potentially be pruned, as long as 529 // they're not used in the Zip. 530 // TODO(rytaft): It may be possible to prune Zip columns, but we need to 531 // make sure that we still get the correct number of rows in the output. 532 projectSet := e.(*memo.ProjectSetExpr) 533 relProps.Rule.PruneCols = DerivePruneCols(projectSet.Input).Copy() 534 usedCols := projectSet.Zip.OuterCols() 535 relProps.Rule.PruneCols.DifferenceWith(usedCols) 536 537 case opt.UnionAllOp: 538 // Pruning can be beneficial as long as one of our inputs has advertised pruning, 539 // so that we can push down the project and eliminate the advertisement. 540 u := e.(*memo.UnionAllExpr) 541 pruneFromLeft := opt.TranslateColSet(DerivePruneCols(u.Left), u.LeftCols, u.OutCols) 542 pruneFromRight := opt.TranslateColSet(DerivePruneCols(u.Right), u.RightCols, u.OutCols) 543 relProps.Rule.PruneCols = pruneFromLeft.Union(pruneFromRight) 544 545 case opt.WindowOp: 546 win := e.(*memo.WindowExpr) 547 relProps.Rule.PruneCols = DerivePruneCols(win.Input).Copy() 548 relProps.Rule.PruneCols.DifferenceWith(win.Partition) 549 relProps.Rule.PruneCols.DifferenceWith(win.Ordering.ColSet()) 550 for _, w := range win.Windows { 551 relProps.Rule.PruneCols.Add(w.Col) 552 relProps.Rule.PruneCols.DifferenceWith(w.ScalarProps().OuterCols) 553 } 554 555 case opt.UpdateOp, opt.UpsertOp, opt.DeleteOp: 556 // Find the columns that would need to be fetched, if no returning 557 // clause were present. 558 withoutReturningPrivate := *e.Private().(*memo.MutationPrivate) 559 withoutReturningPrivate.ReturnCols = opt.ColList{} 560 neededCols := neededMutationFetchCols(e.Memo(), e.Op(), &withoutReturningPrivate) 561 562 // Only the "free" RETURNING columns can be pruned away (i.e. the columns 563 // required by the mutation only because they're being returned). 564 relProps.Rule.PruneCols = relProps.OutputCols.Difference(neededCols) 565 566 case opt.WithOp: 567 // WithOp passes through its input unchanged, so it has the same pruning 568 // characteristics as its input. 569 relProps.Rule.PruneCols = DerivePruneCols(e.(*memo.WithExpr).Main) 570 571 default: 572 // Don't allow any columns to be pruned, since that would trigger the 573 // creation of a wrapper Project around an operator that does not have 574 // a pruning rule that will eliminate that Project. 575 } 576 577 return relProps.Rule.PruneCols 578 } 579 580 // CanPruneMutationReturnCols checks whether the mutation's return columns can 581 // be pruned. This is the pre-condition for the PruneMutationReturnCols rule. 582 func (c *CustomFuncs) CanPruneMutationReturnCols( 583 private *memo.MutationPrivate, needed opt.ColSet, 584 ) bool { 585 if private.ReturnCols == nil { 586 return false 587 } 588 589 tabID := c.mem.Metadata().TableMeta(private.Table).MetaID 590 for i := range private.ReturnCols { 591 if private.ReturnCols[i] != 0 && !needed.Contains(tabID.ColumnID(i)) { 592 return true 593 } 594 } 595 596 for _, passthroughCol := range private.PassthroughCols { 597 if passthroughCol != 0 && !needed.Contains(passthroughCol) { 598 return true 599 } 600 } 601 602 return false 603 } 604 605 // PruneMutationReturnCols rewrites the given mutation private to no longer 606 // keep ReturnCols that are not referenced by the RETURNING clause or are not 607 // part of the primary key. The caller must have already done the analysis to 608 // prove that such columns exist, by calling CanPruneMutationReturnCols. 609 func (c *CustomFuncs) PruneMutationReturnCols( 610 private *memo.MutationPrivate, needed opt.ColSet, 611 ) *memo.MutationPrivate { 612 newPrivate := *private 613 newReturnCols := make(opt.ColList, len(private.ReturnCols)) 614 newPassthroughCols := make(opt.ColList, 0, len(private.PassthroughCols)) 615 tabID := c.mem.Metadata().TableMeta(private.Table).MetaID 616 617 // Prune away the ReturnCols that are unused. 618 for i := range private.ReturnCols { 619 if needed.Contains(tabID.ColumnID(i)) { 620 newReturnCols[i] = private.ReturnCols[i] 621 } 622 } 623 624 // Prune away the PassthroughCols that are unused. 625 for _, passthroughCol := range private.PassthroughCols { 626 if passthroughCol != 0 && needed.Contains(passthroughCol) { 627 newPassthroughCols = append(newPassthroughCols, passthroughCol) 628 } 629 } 630 631 newPrivate.ReturnCols = newReturnCols 632 newPrivate.PassthroughCols = newPassthroughCols 633 return &newPrivate 634 } 635 636 // MutationTable returns the table upon which the mutation is applied. 637 // CHECK 638 func (c *CustomFuncs) MutationTable(private *memo.MutationPrivate) opt.TableID { 639 return private.Table 640 } 641 642 // NeededColMapLeft returns the subset of a SetPrivate's LeftCols that corresponds to the 643 // needed subset of OutCols. This is useful for pruning columns in set operations. 644 func (c *CustomFuncs) NeededColMapLeft(needed opt.ColSet, set *memo.SetPrivate) opt.ColSet { 645 return opt.TranslateColSet(needed, set.OutCols, set.LeftCols) 646 } 647 648 // NeededColMapRight returns the subset of a SetPrivate's RightCols that corresponds to the 649 // needed subset of OutCols. This is useful for pruning columns in set operations. 650 func (c *CustomFuncs) NeededColMapRight(needed opt.ColSet, set *memo.SetPrivate) opt.ColSet { 651 return opt.TranslateColSet(needed, set.OutCols, set.RightCols) 652 }