github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sqlbase/computed_exprs.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 sqlbase
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sem/transform"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    22  )
    23  
    24  // RowIndexedVarContainer is used to evaluate expressions over various rows.
    25  type RowIndexedVarContainer struct {
    26  	CurSourceRow tree.Datums
    27  
    28  	// Because the rows we have might not be permuted in the same way as the
    29  	// original table, we need to store a mapping between them.
    30  
    31  	Cols    []ColumnDescriptor
    32  	Mapping map[ColumnID]int
    33  }
    34  
    35  var _ tree.IndexedVarContainer = &RowIndexedVarContainer{}
    36  
    37  // IndexedVarEval implements tree.IndexedVarContainer.
    38  func (r *RowIndexedVarContainer) IndexedVarEval(
    39  	idx int, ctx *tree.EvalContext,
    40  ) (tree.Datum, error) {
    41  	rowIdx, ok := r.Mapping[r.Cols[idx].ID]
    42  	if !ok {
    43  		return tree.DNull, nil
    44  	}
    45  	return r.CurSourceRow[rowIdx], nil
    46  }
    47  
    48  // IndexedVarResolvedType implements tree.IndexedVarContainer.
    49  func (*RowIndexedVarContainer) IndexedVarResolvedType(idx int) *types.T {
    50  	panic("unsupported")
    51  }
    52  
    53  // IndexedVarNodeFormatter implements tree.IndexedVarContainer.
    54  func (*RowIndexedVarContainer) IndexedVarNodeFormatter(idx int) tree.NodeFormatter {
    55  	return nil
    56  }
    57  
    58  // descContainer is a helper type that implements tree.IndexedVarContainer; it
    59  // is used to type check computed columns and does not support evaluation.
    60  type descContainer struct {
    61  	cols []ColumnDescriptor
    62  }
    63  
    64  func (j *descContainer) IndexedVarEval(idx int, ctx *tree.EvalContext) (tree.Datum, error) {
    65  	panic("unsupported")
    66  }
    67  
    68  func (j *descContainer) IndexedVarResolvedType(idx int) *types.T {
    69  	return j.cols[idx].Type
    70  }
    71  
    72  func (*descContainer) IndexedVarNodeFormatter(idx int) tree.NodeFormatter {
    73  	return nil
    74  }
    75  
    76  // CannotWriteToComputedColError constructs a write error for a computed column.
    77  func CannotWriteToComputedColError(colName string) error {
    78  	return pgerror.Newf(pgcode.ObjectNotInPrerequisiteState,
    79  		"cannot write directly to computed column %q", tree.ErrNameString(colName))
    80  }
    81  
    82  // MakeComputedExprs returns a slice of the computed expressions for the
    83  // slice of input column descriptors, or nil if none of the input column
    84  // descriptors have computed expressions.
    85  // The length of the result slice matches the length of the input column
    86  // descriptors. For every column that has no computed expression, a NULL
    87  // expression is reported.
    88  // addingCols indicates if the input column descriptors are being added
    89  // and allows type checking of the compute expressions to reference
    90  // input columns earlier in the slice.
    91  func MakeComputedExprs(
    92  	ctx context.Context,
    93  	cols []ColumnDescriptor,
    94  	tableDesc *ImmutableTableDescriptor,
    95  	tn *tree.TableName,
    96  	txCtx *transform.ExprTransformContext,
    97  	evalCtx *tree.EvalContext,
    98  	addingCols bool,
    99  ) ([]tree.TypedExpr, error) {
   100  	// Check to see if any of the columns have computed expressions. If there
   101  	// are none, we don't bother with constructing the map as the expressions
   102  	// are all NULL.
   103  	haveComputed := false
   104  	for i := range cols {
   105  		if cols[i].IsComputed() {
   106  			haveComputed = true
   107  			break
   108  		}
   109  	}
   110  	if !haveComputed {
   111  		return nil, nil
   112  	}
   113  
   114  	// Build the computed expressions map from the parsed statement.
   115  	computedExprs := make([]tree.TypedExpr, 0, len(cols))
   116  	exprStrings := make([]string, 0, len(cols))
   117  	for i := range cols {
   118  		col := &cols[i]
   119  		if col.IsComputed() {
   120  			exprStrings = append(exprStrings, *col.ComputeExpr)
   121  		}
   122  	}
   123  	exprs, err := parser.ParseExprs(exprStrings)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	// We need an ivarHelper and sourceInfo, unlike DEFAULT, since computed
   129  	// columns can reference other columns and thus need to be able to resolve
   130  	// column names (at this stage they only need to resolve the types so that
   131  	// the expressions can be typechecked - we have no need to evaluate them).
   132  	iv := &descContainer{tableDesc.Columns}
   133  	ivarHelper := tree.MakeIndexedVarHelper(iv, len(tableDesc.Columns))
   134  
   135  	source := NewSourceInfoForSingleTable(*tn, ResultColumnsFromColDescs(tableDesc.GetID(), tableDesc.Columns))
   136  	semaCtx := tree.MakeSemaContext()
   137  	semaCtx.IVarContainer = iv
   138  
   139  	addColumnInfo := func(col *ColumnDescriptor) {
   140  		ivarHelper.AppendSlot()
   141  		iv.cols = append(iv.cols, *col)
   142  		newCols := ResultColumnsFromColDescs(tableDesc.GetID(), []ColumnDescriptor{*col})
   143  		source.SourceColumns = append(source.SourceColumns, newCols...)
   144  	}
   145  
   146  	compExprIdx := 0
   147  	for i := range cols {
   148  		col := &cols[i]
   149  		if !col.IsComputed() {
   150  			computedExprs = append(computedExprs, tree.DNull)
   151  			if addingCols {
   152  				addColumnInfo(col)
   153  			}
   154  			continue
   155  		}
   156  		expr, err := ResolveNames(
   157  			exprs[compExprIdx], source, ivarHelper, evalCtx.SessionData.SearchPath)
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  
   162  		typedExpr, err := tree.TypeCheck(ctx, expr, &semaCtx, col.Type)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  		if typedExpr, err = txCtx.NormalizeExpr(evalCtx, typedExpr); err != nil {
   167  			return nil, err
   168  		}
   169  		computedExprs = append(computedExprs, typedExpr)
   170  		compExprIdx++
   171  		if addingCols {
   172  			addColumnInfo(col)
   173  		}
   174  	}
   175  	return computedExprs, nil
   176  }