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  }