github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/project_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  	"fmt"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    20  	"github.com/cockroachdb/errors"
    21  )
    22  
    23  // CanMergeProjections returns true if the outer Projections operator never
    24  // references any of the inner Projections columns. If true, then the outer does
    25  // not depend on the inner, and the two can be merged into a single set.
    26  func (c *CustomFuncs) CanMergeProjections(outer, inner memo.ProjectionsExpr) bool {
    27  	innerCols := c.ProjectionCols(inner)
    28  	for i := range outer {
    29  		if outer[i].ScalarProps().OuterCols.Intersects(innerCols) {
    30  			return false
    31  		}
    32  	}
    33  	return true
    34  }
    35  
    36  // MergeProjections concatenates the synthesized columns from the outer
    37  // Projections operator, and the synthesized columns from the inner Projections
    38  // operator that are passed through by the outer. Note that the outer
    39  // synthesized columns must never contain references to the inner synthesized
    40  // columns; this can be verified by first calling CanMergeProjections.
    41  func (c *CustomFuncs) MergeProjections(
    42  	outer, inner memo.ProjectionsExpr, passthrough opt.ColSet,
    43  ) memo.ProjectionsExpr {
    44  	// No need to recompute properties on the new projections, since they should
    45  	// still be valid.
    46  	newProjections := make(memo.ProjectionsExpr, len(outer), len(outer)+len(inner))
    47  	copy(newProjections, outer)
    48  	for i := range inner {
    49  		item := &inner[i]
    50  		if passthrough.Contains(item.Col) {
    51  			newProjections = append(newProjections, *item)
    52  		}
    53  	}
    54  	return newProjections
    55  }
    56  
    57  // MergeProjectWithValues merges a Project operator with its input Values
    58  // operator. This is only possible in certain circumstances, which are described
    59  // in the MergeProjectWithValues rule comment.
    60  //
    61  // Values columns that are part of the Project passthrough columns are retained
    62  // in the final Values operator, and Project synthesized columns are added to
    63  // it. Any unreferenced Values columns are discarded. For example:
    64  //
    65  //   SELECT column1, 3 FROM (VALUES (1, 2))
    66  //   =>
    67  //   (VALUES (1, 3))
    68  //
    69  func (c *CustomFuncs) MergeProjectWithValues(
    70  	projections memo.ProjectionsExpr, passthrough opt.ColSet, input memo.RelExpr,
    71  ) memo.RelExpr {
    72  	newExprs := make(memo.ScalarListExpr, 0, len(projections)+passthrough.Len())
    73  	newTypes := make([]*types.T, 0, len(newExprs))
    74  	newCols := make(opt.ColList, 0, len(newExprs))
    75  
    76  	values := input.(*memo.ValuesExpr)
    77  	tuple := values.Rows[0].(*memo.TupleExpr)
    78  	for i, colID := range values.Cols {
    79  		if passthrough.Contains(colID) {
    80  			newExprs = append(newExprs, tuple.Elems[i])
    81  			newTypes = append(newTypes, tuple.Elems[i].DataType())
    82  			newCols = append(newCols, colID)
    83  		}
    84  	}
    85  
    86  	for i := range projections {
    87  		item := &projections[i]
    88  		newExprs = append(newExprs, item.Element)
    89  		newTypes = append(newTypes, item.Element.DataType())
    90  		newCols = append(newCols, item.Col)
    91  	}
    92  
    93  	tupleTyp := types.MakeTuple(newTypes)
    94  	rows := memo.ScalarListExpr{c.f.ConstructTuple(newExprs, tupleTyp)}
    95  	return c.f.ConstructValues(rows, &memo.ValuesPrivate{
    96  		Cols: newCols,
    97  		ID:   values.ID,
    98  	})
    99  }
   100  
   101  // CanUnnestTuplesFromValues returns true if the Values operator has a single
   102  // column containing tuples that can be unfolded into multiple columns.
   103  //
   104  // This is the case if:
   105  //
   106  // 	1. The Values operator has exactly one output column.
   107  //
   108  // 	2. The single output column is of type tuple.
   109  //
   110  // 	3. There is at least one row.
   111  //
   112  // 	4. All tuples in the single column are either TupleExpr's or ConstExpr's
   113  //     that wrap DTuples, as opposed to dynamically generated tuples.
   114  //
   115  func (c *CustomFuncs) CanUnnestTuplesFromValues(expr memo.RelExpr) bool {
   116  	values := expr.(*memo.ValuesExpr)
   117  	if !c.HasOneCol(expr) {
   118  		return false
   119  	}
   120  	colTypeFam := c.mem.Metadata().ColumnMeta(values.Cols[0]).Type.Family()
   121  	if colTypeFam != types.TupleFamily {
   122  		return false
   123  	}
   124  	if len(values.Rows) < 1 {
   125  		return false
   126  	}
   127  	for _, row := range values.Rows {
   128  		if !c.IsStaticTuple(row.(*memo.TupleExpr).Elems[0]) {
   129  			return false
   130  		}
   131  	}
   132  	return true
   133  }
   134  
   135  // OnlyTupleColumnsAccessed ensures that the input ProjectionsExpr contains no
   136  // direct references to the tuple represented by the given ColumnID.
   137  func (c *CustomFuncs) OnlyTupleColumnsAccessed(
   138  	projections memo.ProjectionsExpr, tupleCol opt.ColumnID,
   139  ) bool {
   140  	var check func(expr opt.Expr) bool
   141  	check = func(expr opt.Expr) bool {
   142  		switch t := expr.(type) {
   143  		case *memo.ColumnAccessExpr:
   144  			switch t.Input.(type) {
   145  			case *memo.VariableExpr:
   146  				return true
   147  			}
   148  
   149  		case *memo.VariableExpr:
   150  			return t.Col != tupleCol
   151  		}
   152  		for i, n := 0, expr.ChildCount(); i < n; i++ {
   153  			if !check(expr.Child(i)) {
   154  				return false
   155  			}
   156  		}
   157  		return true
   158  	}
   159  
   160  	for i := range projections {
   161  		if !check(projections[i].Element) {
   162  			return false
   163  		}
   164  	}
   165  
   166  	return true
   167  }
   168  
   169  // MakeColsForUnnestTuples takes in the ColumnID of a tuple column and adds new
   170  // columns to metadata corresponding to each field in the tuple. A ColList
   171  // containing these new columns is returned.
   172  func (c *CustomFuncs) MakeColsForUnnestTuples(tupleColID opt.ColumnID) opt.ColList {
   173  	mem := c.mem.Metadata()
   174  	tupleType := c.mem.Metadata().ColumnMeta(tupleColID).Type
   175  
   176  	// Create a new column for each position in the tuple. Add it to outColIDs.
   177  	tupleLen := len(tupleType.TupleContents())
   178  	tupleAlias := mem.ColumnMeta(tupleColID).Alias
   179  	outColIDs := make(opt.ColList, tupleLen)
   180  	for i := 0; i < tupleLen; i++ {
   181  		newAlias := fmt.Sprintf("%s_%d", tupleAlias, i+1)
   182  		newColID := mem.AddColumn(newAlias, tupleType.TupleContents()[i])
   183  		outColIDs[i] = newColID
   184  	}
   185  	return outColIDs
   186  }
   187  
   188  // UnnestTuplesFromValues takes in a Values operator that has a single column
   189  // of tuples and a ColList corresponding to each tuple field. It returns a new
   190  // Values operator with the tuple expanded out into the Values rows.
   191  // For example, these rows:
   192  //
   193  //   ((1, 2),)
   194  //   ((3, 4),)
   195  //
   196  // would be unnested as:
   197  //
   198  //   (1, 2)
   199  //   (3, 4)
   200  //
   201  func (c *CustomFuncs) UnnestTuplesFromValues(
   202  	expr memo.RelExpr, valuesCols opt.ColList,
   203  ) memo.RelExpr {
   204  	values := expr.(*memo.ValuesExpr)
   205  	tupleColID := values.Cols[0]
   206  	tupleType := c.mem.Metadata().ColumnMeta(tupleColID).Type
   207  	outTuples := make(memo.ScalarListExpr, len(values.Rows))
   208  
   209  	// Pull the inner tuples out of the single column of the Values operator and
   210  	// put them into a ScalarListExpr to be used in the new Values operator.
   211  	for i, row := range values.Rows {
   212  		outerTuple := row.(*memo.TupleExpr)
   213  		switch t := outerTuple.Elems[0].(type) {
   214  		case *memo.TupleExpr:
   215  			outTuples[i] = t
   216  
   217  		case *memo.ConstExpr:
   218  			dTuple := t.Value.(*tree.DTuple)
   219  			tupleVals := make(memo.ScalarListExpr, len(dTuple.D))
   220  			for i, v := range dTuple.D {
   221  				val := c.f.ConstructConstVal(v, tupleType.TupleContents()[i])
   222  				tupleVals[i] = val
   223  			}
   224  			outTuples[i] = c.f.ConstructTuple(tupleVals, tupleType)
   225  
   226  		default:
   227  			panic(errors.AssertionFailedf("unhandled input op: %T", t))
   228  		}
   229  	}
   230  
   231  	// Return new ValuesExpr with new tuples.
   232  	valuesPrivate := &memo.ValuesPrivate{Cols: valuesCols, ID: c.mem.Metadata().NextUniqueID()}
   233  	return c.f.ConstructValues(outTuples, valuesPrivate)
   234  }
   235  
   236  // FoldTupleColumnAccess constructs a new ProjectionsExpr from the old one with
   237  // any ColumnAccess operators that refer to the original tuple column (oldColID)
   238  // replaced by new columns from the output of the given ValuesExpr.
   239  func (c *CustomFuncs) FoldTupleColumnAccess(
   240  	projections memo.ProjectionsExpr, valuesCols opt.ColList, oldColID opt.ColumnID,
   241  ) memo.ProjectionsExpr {
   242  	newProjections := make(memo.ProjectionsExpr, len(projections))
   243  
   244  	// Recursively traverses a ProjectionsItem element and replaces references to
   245  	// positions in the tuple rows with one of the newly constructed columns.
   246  	var replace ReplaceFunc
   247  	replace = func(nd opt.Expr) opt.Expr {
   248  		if colAccess, ok := nd.(*memo.ColumnAccessExpr); ok {
   249  			if variable, ok := colAccess.Input.(*memo.VariableExpr); ok {
   250  				// Skip past references to columns other than the input tuple column.
   251  				if variable.Col == oldColID {
   252  					return c.f.ConstructVariable(valuesCols[int(colAccess.Idx)])
   253  				}
   254  			}
   255  		}
   256  		return c.f.Replace(nd, replace)
   257  	}
   258  
   259  	// Construct and return a new ProjectionsExpr using the new ColumnIDs.
   260  	for i := range projections {
   261  		projection := &projections[i]
   262  		newProjections[i] = c.f.ConstructProjectionsItem(
   263  			replace(projection.Element).(opt.ScalarExpr), projection.Col)
   264  	}
   265  	return newProjections
   266  }
   267  
   268  // CanPushColumnRemappingIntoValues returns true if there is at least one
   269  // ProjectionsItem for which the following conditions hold:
   270  //
   271  // 1. The ProjectionsItem remaps an output column from the given ValuesExpr.
   272  //
   273  // 2. The Values output column being remapped is not in the passthrough set.
   274  //
   275  func (c *CustomFuncs) CanPushColumnRemappingIntoValues(
   276  	projections memo.ProjectionsExpr, passthrough opt.ColSet, values memo.RelExpr,
   277  ) bool {
   278  	outputCols := values.(*memo.ValuesExpr).Relational().OutputCols
   279  	for i := range projections {
   280  		if variable, ok := projections[i].Element.(*memo.VariableExpr); ok {
   281  			if !passthrough.Contains(variable.Col) && outputCols.Contains(variable.Col) {
   282  				return true
   283  			}
   284  		}
   285  	}
   286  	return false
   287  }
   288  
   289  // PushColumnRemappingIntoValues folds ProjectionsItems into the passthrough set
   290  // if all they do is remap output columns from the ValuesExpr input. The Values
   291  // output columns are replaced by the corresponding columns from the folded
   292  // ProjectionsItems.
   293  //
   294  // Example:
   295  // project
   296  //  ├── columns: x:2!null
   297  //  ├── values
   298  //  │    ├── columns: column1:1!null
   299  //  │    ├── cardinality: [2 - 2]
   300  //  │    ├── (1,)
   301  //  │    └── (2,)
   302  //  └── projections
   303  //       └── column1:1 [as=x:2, outer=(1)]
   304  // =>
   305  // project
   306  //  ├── columns: x:2!null
   307  //  └── values
   308  //       ├── columns: x:2!null
   309  //       ├── cardinality: [2 - 2]
   310  //       ├── (1,)
   311  //       └── (2,)
   312  //
   313  // This allows other rules to fire. In the above example, EliminateProject can
   314  // now remove the Project altogether.
   315  func (c *CustomFuncs) PushColumnRemappingIntoValues(
   316  	oldInput memo.RelExpr, oldProjections memo.ProjectionsExpr, oldPassthrough opt.ColSet,
   317  ) memo.RelExpr {
   318  	oldValues := oldInput.(*memo.ValuesExpr)
   319  	oldValuesCols := oldValues.Relational().OutputCols
   320  	newPassthrough := oldPassthrough.Copy()
   321  	replacementCols := make(map[opt.ColumnID]opt.ColumnID)
   322  	var newProjections memo.ProjectionsExpr
   323  
   324  	// Construct the new ProjectionsExpr and passthrough columns. Keep track of
   325  	// which Values columns are to be replaced.
   326  	for i := range oldProjections {
   327  		oldItem := &oldProjections[i]
   328  
   329  		// A column can be replaced if the following conditions hold:
   330  		// 1. The current ProjectionsItem contains a VariableExpr.
   331  		// 2. The VariableExpr references a column from the ValuesExpr.
   332  		// 3. The column has not already been assigned a replacement.
   333  		// 4. The column is not a passthrough column.
   334  		if v, ok := oldItem.Element.(*memo.VariableExpr); ok {
   335  			if targetCol := v.Col; oldValuesCols.Contains(targetCol) {
   336  				if replacementCols[targetCol] == 0 {
   337  					if !newPassthrough.Contains(targetCol) {
   338  						// The conditions for column replacement have been met. Map the old
   339  						// Values output column to its replacement and add the replacement
   340  						// to newPassthrough so it will become a passthrough column.
   341  						// Continue so that no corresponding ProjectionsItem is added to
   342  						// newProjections.
   343  						replacementCols[targetCol] = oldItem.Col
   344  						newPassthrough.Add(oldItem.Col)
   345  						continue
   346  					}
   347  				}
   348  			}
   349  		}
   350  		// The current ProjectionsItem cannot be folded into newPassthrough because
   351  		// the above conditions do not hold. Simply add it to newProjections. Later,
   352  		// every ProjectionsItem will be recursively traversed and any references to
   353  		// columns that are in replacementCols will be replaced.
   354  		newProjections = append(newProjections, *oldItem)
   355  	}
   356  
   357  	// Recursively traverses a ProjectionsItem element and replaces references to
   358  	// old ValuesExpr columns with the replacement columns. This ensures that any
   359  	// remaining references to old columns are replaced. For example:
   360  	//
   361  	//   WITH t AS (SELECT x, x FROM (VALUES (1)) f(x)) SELECT * FROM t;
   362  	//
   363  	// The "x" column of the Values operator will be mapped to the first column of
   364  	// t. This first column will become a passthrough column. Now, the remaining
   365  	// reference to "x" in the second column of t needs to be replaced by the new
   366  	// passthrough column.
   367  	var replace ReplaceFunc
   368  	replace = func(nd opt.Expr) opt.Expr {
   369  		switch t := nd.(type) {
   370  		case *memo.VariableExpr:
   371  			if replaceCol := replacementCols[t.Col]; replaceCol != 0 {
   372  				return c.f.ConstructVariable(replaceCol)
   373  			}
   374  		}
   375  		return c.f.Replace(nd, replace)
   376  	}
   377  
   378  	// Traverse each element in newProjections and replace col references as
   379  	// dictated by replacementCols.
   380  	for i := range newProjections {
   381  		item := &newProjections[i]
   382  		newProjections[i] = c.f.ConstructProjectionsItem(
   383  			replace(item.Element).(opt.ScalarExpr), item.Col)
   384  	}
   385  
   386  	// Replace all columns in newValuesColList that have been remapped by the old
   387  	// ProjectionsExpr.
   388  	oldValuesColList := oldValues.Cols
   389  	newValuesColList := make(opt.ColList, len(oldValuesColList))
   390  	for i := range newValuesColList {
   391  		if replaceCol := replacementCols[oldValuesColList[i]]; replaceCol != 0 {
   392  			newValuesColList[i] = replaceCol
   393  		} else {
   394  			newValuesColList[i] = oldValuesColList[i]
   395  		}
   396  	}
   397  
   398  	// Construct a new ValuesExpr with the replaced cols.
   399  	newValues := c.f.ConstructValues(
   400  		oldValues.Rows,
   401  		&memo.ValuesPrivate{Cols: newValuesColList, ID: c.f.Metadata().NextUniqueID()})
   402  
   403  	// Construct and return a new ProjectExpr with the new ValuesExpr as input.
   404  	return c.f.ConstructProject(newValues, newProjections, newPassthrough)
   405  }
   406  
   407  // IsStaticTuple returns true if the given ScalarExpr is either a TupleExpr or a
   408  // ConstExpr wrapping a DTuple. Expressions within a static tuple can be
   409  // determined during planning:
   410  //
   411  //   (1, 2)
   412  //   (x, y)
   413  //
   414  // By contrast, expressions within a dynamic tuple can only be determined at
   415  // run-time:
   416  //
   417  //   SELECT (SELECT (x, y) FROM xy)
   418  //
   419  // Here, if there are 0 rows in xy, the tuple value will be NULL. Or, if there
   420  // is more than one row in xy, a dynamic error will be raised.
   421  func (c *CustomFuncs) IsStaticTuple(expr opt.ScalarExpr) bool {
   422  	switch t := expr.(type) {
   423  	case *memo.TupleExpr:
   424  		return true
   425  
   426  	case *memo.ConstExpr:
   427  		if _, ok := t.Value.(*tree.DTuple); ok {
   428  			return true
   429  		}
   430  	}
   431  	return false
   432  }